1145184Sglebius/*
2145184Sglebius * Copyright (c) 2000-2002, Boris Popov
3145184Sglebius * All rights reserved.
4145184Sglebius *
5145184Sglebius * Redistribution and use in source and binary forms, with or without
6145184Sglebius * modification, are permitted provided that the following conditions
7145184Sglebius * are met:
8145184Sglebius * 1. Redistributions of source code must retain the above copyright
9145184Sglebius *    notice, this list of conditions and the following disclaimer.
10155768Sceri * 2. Redistributions in binary form must reproduce the above copyright
11145184Sglebius *    notice, this list of conditions and the following disclaimer in the
12155458Sglebius *    documentation and/or other materials provided with the distribution.
13145184Sglebius * 3. All advertising materials mentioning features or use of this software
14145184Sglebius *    must display the following acknowledgement:
15216300Ssyrinx *    This product includes software developed by Boris Popov.
16216300Ssyrinx * 4. Neither the name of the author nor the names of any co-contributors
17216300Ssyrinx *    may be used to endorse or promote products derived from this software
18216300Ssyrinx *    without specific prior written permission.
19216300Ssyrinx *
20216300Ssyrinx * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21216300Ssyrinx * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22216300Ssyrinx * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23216300Ssyrinx * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24145184Sglebius * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25145184Sglebius * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26165685Smaxim * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27165685Smaxim * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28145184Sglebius * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29145184Sglebius * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30145184Sglebius * SUCH DAMAGE.
31216595Ssyrinx *
32216300Ssyrinx * $Id: ctx.c,v 1.24 2002/04/13 14:35:28 bp Exp $
33216595Ssyrinx * $FreeBSD$
34216595Ssyrinx */
35216300Ssyrinx#include <sys/param.h>
36216300Ssyrinx#include <sys/sysctl.h>
37216300Ssyrinx#include <sys/ioctl.h>
38216300Ssyrinx#include <sys/time.h>
39216300Ssyrinx#include <sys/mount.h>
40216300Ssyrinx#include <fcntl.h>
41216300Ssyrinx#include <ctype.h>
42145184Sglebius#include <errno.h>
43216595Ssyrinx#include <stdio.h>
44216595Ssyrinx#include <string.h>
45216595Ssyrinx#include <stdlib.h>
46216595Ssyrinx#include <pwd.h>
47216595Ssyrinx#include <grp.h>
48216595Ssyrinx#include <unistd.h>
49216595Ssyrinx#include <sys/iconv.h>
50216595Ssyrinx
51216595Ssyrinx#define NB_NEEDRESOLVER
52216595Ssyrinx
53216595Ssyrinx#include <netsmb/smb_lib.h>
54216595Ssyrinx#include <netsmb/netbios.h>
55216595Ssyrinx#include <netsmb/nb_lib.h>
56216595Ssyrinx#include <netsmb/smb_conn.h>
57216595Ssyrinx#include <cflib.h>
58216595Ssyrinx
59216595Ssyrinx/*
60216595Ssyrinx * Prescan command line for [-U user] argument
61216595Ssyrinx * and fill context with defaults
62216595Ssyrinx */
63216300Ssyrinxint
64216300Ssyrinxsmb_ctx_init(struct smb_ctx *ctx, int argc, char *argv[],
65216300Ssyrinx	int minlevel, int maxlevel, int sharetype)
66216300Ssyrinx{
67216300Ssyrinx	int  opt, error = 0;
68216300Ssyrinx	uid_t euid;
69216595Ssyrinx	const char *arg, *cp;
70216300Ssyrinx	struct passwd *pwd;
71216595Ssyrinx
72216595Ssyrinx	bzero(ctx,sizeof(*ctx));
73216300Ssyrinx	error = nb_ctx_create(&ctx->ct_nb);
74216300Ssyrinx	if (error)
75145184Sglebius		return error;
76145184Sglebius	ctx->ct_fd = -1;
77145184Sglebius	ctx->ct_parsedlevel = SMBL_NONE;
78145184Sglebius	ctx->ct_minlevel = minlevel;
79145184Sglebius	ctx->ct_maxlevel = maxlevel;
80145184Sglebius	ctx->ct_smbtcpport = SMB_TCP_PORT;
81154186Sharti
82154186Sharti	ctx->ct_ssn.ioc_opt = SMBVOPT_CREATE;
83154186Sharti	ctx->ct_ssn.ioc_timeout = 15;
84154186Sharti	ctx->ct_ssn.ioc_retrycount = 4;
85154186Sharti	ctx->ct_ssn.ioc_owner = SMBM_ANY_OWNER;
86154186Sharti	ctx->ct_ssn.ioc_group = SMBM_ANY_GROUP;
87154186Sharti	ctx->ct_ssn.ioc_mode = SMBM_EXEC;
88154186Sharti	ctx->ct_ssn.ioc_rights = SMBM_DEFAULT;
89154186Sharti
90154186Sharti	ctx->ct_sh.ioc_opt = SMBVOPT_CREATE;
91154186Sharti	ctx->ct_sh.ioc_owner = SMBM_ANY_OWNER;
92154186Sharti	ctx->ct_sh.ioc_group = SMBM_ANY_GROUP;
93145184Sglebius	ctx->ct_sh.ioc_mode = SMBM_EXEC;
94154186Sharti	ctx->ct_sh.ioc_rights = SMBM_DEFAULT;
95145184Sglebius	ctx->ct_sh.ioc_owner = SMBM_ANY_OWNER;
96145184Sglebius	ctx->ct_sh.ioc_group = SMBM_ANY_GROUP;
97145184Sglebius
98163823Sharti	nb_ctx_setscope(ctx->ct_nb, "");
99145184Sglebius	euid = geteuid();
100145184Sglebius	if ((pwd = getpwuid(euid)) != NULL) {
101145184Sglebius		smb_ctx_setuser(ctx, pwd->pw_name);
102145184Sglebius		endpwent();
103145184Sglebius	} else if (euid == 0)
104145184Sglebius		smb_ctx_setuser(ctx, "root");
105145184Sglebius	else
106145184Sglebius		return 0;
107145184Sglebius	if (argv == NULL)
108145184Sglebius		return 0;
109145184Sglebius	for (opt = 1; opt < argc; opt++) {
110145184Sglebius		cp = argv[opt];
111145184Sglebius		if (strncmp(cp, "//", 2) != 0)
112145184Sglebius			continue;
113145184Sglebius		error = smb_ctx_parseunc(ctx, cp, sharetype, (const char**)&cp);
114145184Sglebius		if (error)
115145184Sglebius			return error;
116216300Ssyrinx		ctx->ct_uncnext = cp;
117216300Ssyrinx		break;
118216300Ssyrinx	}
119216300Ssyrinx	while (error == 0 && (opt = cf_getopt(argc, argv, ":E:L:U:")) != -1) {
120216300Ssyrinx		arg = cf_optarg;
121216300Ssyrinx		switch (opt) {
122216300Ssyrinx		    case 'E':
123216300Ssyrinx			error = smb_ctx_setcharset(ctx, arg);
124216300Ssyrinx			if (error)
125216300Ssyrinx				return error;
126216300Ssyrinx			break;
127216300Ssyrinx		    case 'L':
128216300Ssyrinx			error = nls_setlocale(arg);
129216300Ssyrinx			if (error)
130216300Ssyrinx				break;
131216300Ssyrinx			break;
132216300Ssyrinx		    case 'U':
133216300Ssyrinx			error = smb_ctx_setuser(ctx, arg);
134216300Ssyrinx			break;
135216300Ssyrinx		}
136216300Ssyrinx	}
137216300Ssyrinx	cf_optind = cf_optreset = 1;
138216300Ssyrinx	return error;
139216300Ssyrinx}
140216300Ssyrinx
141216301Ssyrinxvoid
142216300Ssyrinxsmb_ctx_done(struct smb_ctx *ctx)
143216300Ssyrinx{
144216300Ssyrinx	if (ctx->ct_ssn.ioc_server)
145216300Ssyrinx		nb_snbfree(ctx->ct_ssn.ioc_server);
146216300Ssyrinx	if (ctx->ct_ssn.ioc_local)
147216300Ssyrinx		nb_snbfree(ctx->ct_ssn.ioc_local);
148216300Ssyrinx	if (ctx->ct_srvaddr)
149216300Ssyrinx		free(ctx->ct_srvaddr);
150216300Ssyrinx	if (ctx->ct_nb)
151216300Ssyrinx		nb_ctx_done(ctx->ct_nb);
152216300Ssyrinx}
153216300Ssyrinx
154216300Ssyrinxstatic int
155216300Ssyrinxgetsubstring(const char *p, u_char sep, char *dest, int maxlen, const char **next)
156216300Ssyrinx{
157216300Ssyrinx	int len;
158216300Ssyrinx
159216300Ssyrinx	maxlen--;
160216595Ssyrinx	for (len = 0; len < maxlen && *p != sep; p++, len++, dest++) {
161216595Ssyrinx		if (*p == 0)
162216300Ssyrinx			return EINVAL;
163216300Ssyrinx		*dest = *p;
164216595Ssyrinx	}
165216595Ssyrinx	*dest = 0;
166216300Ssyrinx	*next = *p ? p + 1 : p;
167216300Ssyrinx	return 0;
168216595Ssyrinx}
169216595Ssyrinx
170216595Ssyrinx/*
171216595Ssyrinx * Here we expect something like "[proto:]//[user@]host[:psmb[:pnb]][/share][/path]"
172216300Ssyrinx */
173216300Ssyrinxint
174216300Ssyrinxsmb_ctx_parseunc(struct smb_ctx *ctx, const char *unc, int sharetype,
175216300Ssyrinx	const char **next)
176216300Ssyrinx{
177216300Ssyrinx	const char *p = unc;
178216300Ssyrinx	char *p1, *psmb, *pnb;
179216300Ssyrinx	char tmp[1024];
180216300Ssyrinx	int error ;
181216300Ssyrinx
182216300Ssyrinx	ctx->ct_parsedlevel = SMBL_NONE;
183216300Ssyrinx	if (*p++ != '/' || *p++ != '/') {
184216300Ssyrinx		smb_error("UNC should start with '//'", 0);
185216300Ssyrinx		return EINVAL;
186216300Ssyrinx	}
187216300Ssyrinx	p1 = tmp;
188216300Ssyrinx	error = getsubstring(p, '@', p1, sizeof(tmp), &p);
189216300Ssyrinx	if (!error) {
190216300Ssyrinx		if (ctx->ct_maxlevel < SMBL_VC) {
191216300Ssyrinx			smb_error("no user name required", 0);
192216595Ssyrinx			return EINVAL;
193216595Ssyrinx		}
194216300Ssyrinx		if (*p1 == 0) {
195216300Ssyrinx			smb_error("empty user name", 0);
196216300Ssyrinx			return EINVAL;
197216300Ssyrinx		}
198216595Ssyrinx		error = smb_ctx_setuser(ctx, tmp);
199216595Ssyrinx		if (error)
200216595Ssyrinx			return error;
201216300Ssyrinx		ctx->ct_parsedlevel = SMBL_VC;
202216300Ssyrinx	}
203216300Ssyrinx	error = getsubstring(p, '/', p1, sizeof(tmp), &p);
204216300Ssyrinx	if (error) {
205216300Ssyrinx		error = getsubstring(p, '\0', p1, sizeof(tmp), &p);
206216595Ssyrinx		if (error) {
207216595Ssyrinx			smb_error("no server name found", 0);
208216595Ssyrinx			return error;
209216300Ssyrinx		}
210216300Ssyrinx	}
211216300Ssyrinx	if (*p1 == 0) {
212216300Ssyrinx		smb_error("empty server name", 0);
213216300Ssyrinx		return EINVAL;
214216300Ssyrinx	}
215216300Ssyrinx	/*
216216300Ssyrinx	 * Check for port number specification.
217216300Ssyrinx	 */
218216595Ssyrinx	psmb = strchr(tmp, ':');
219216595Ssyrinx	if (psmb) {
220216595Ssyrinx		*psmb++ = '\0';
221216595Ssyrinx		pnb = strchr(psmb, ':');
222216595Ssyrinx		if (pnb) {
223216595Ssyrinx			*pnb++ = '\0';
224216595Ssyrinx			error = smb_ctx_setnbport(ctx, atoi(pnb));
225216595Ssyrinx			if (error) {
226216595Ssyrinx				smb_error("Invalid NetBIOS port number", 0);
227216595Ssyrinx				return error;
228216595Ssyrinx			}
229216595Ssyrinx		}
230216595Ssyrinx		error = smb_ctx_setsmbport(ctx, atoi(psmb));
231216595Ssyrinx		if (error) {
232216595Ssyrinx			smb_error("Invalid SMB port number", 0);
233216595Ssyrinx			return error;
234216595Ssyrinx		}
235216595Ssyrinx	}
236216595Ssyrinx	error = smb_ctx_setserver(ctx, tmp);
237216595Ssyrinx	if (error)
238216595Ssyrinx		return error;
239216595Ssyrinx	if (sharetype == SMB_ST_NONE) {
240216595Ssyrinx		*next = p;
241216595Ssyrinx		return 0;
242216595Ssyrinx	}
243216595Ssyrinx	if (*p != 0 && ctx->ct_maxlevel < SMBL_SHARE) {
244216595Ssyrinx		smb_error("no share name required", 0);
245216595Ssyrinx		return EINVAL;
246216595Ssyrinx	}
247216595Ssyrinx	error = getsubstring(p, '/', p1, sizeof(tmp), &p);
248216595Ssyrinx	if (error) {
249216595Ssyrinx		error = getsubstring(p, '\0', p1, sizeof(tmp), &p);
250216595Ssyrinx		if (error) {
251216595Ssyrinx			smb_error("unexpected end of line", 0);
252216595Ssyrinx			return error;
253216595Ssyrinx		}
254216595Ssyrinx	}
255216595Ssyrinx	if (*p1 == 0 && ctx->ct_minlevel >= SMBL_SHARE) {
256216595Ssyrinx		smb_error("empty share name", 0);
257216595Ssyrinx		return EINVAL;
258216595Ssyrinx	}
259216595Ssyrinx	*next = p;
260216595Ssyrinx	if (*p1 == 0)
261216595Ssyrinx		return 0;
262216595Ssyrinx	error = smb_ctx_setshare(ctx, p1, sharetype);
263216595Ssyrinx	return error;
264216595Ssyrinx}
265216595Ssyrinx
266216595Ssyrinxint
267216595Ssyrinxsmb_ctx_setcharset(struct smb_ctx *ctx, const char *arg)
268216595Ssyrinx{
269216595Ssyrinx	char *cp, *servercs, *localcs;
270216595Ssyrinx	int cslen = sizeof(ctx->ct_ssn.ioc_localcs);
271216595Ssyrinx	int scslen, lcslen, error;
272216595Ssyrinx
273216595Ssyrinx	cp = strchr(arg, ':');
274145184Sglebius	lcslen = cp ? (cp - arg) : 0;
275145184Sglebius	if (lcslen == 0 || lcslen >= cslen) {
276145184Sglebius		smb_error("invalid local charset specification (%s)", 0, arg);
277145184Sglebius		return EINVAL;
278145184Sglebius	}
279145184Sglebius	scslen = (size_t)strlen(++cp);
280145184Sglebius	if (scslen == 0 || scslen >= cslen) {
281145184Sglebius		smb_error("invalid server charset specification (%s)", 0, arg);
282145184Sglebius		return EINVAL;
283145184Sglebius	}
284145184Sglebius	localcs = memcpy(ctx->ct_ssn.ioc_localcs, arg, lcslen);
285154177Sharti	localcs[lcslen] = 0;
286154177Sharti	servercs = strcpy(ctx->ct_ssn.ioc_servercs, cp);
287154177Sharti	error = nls_setrecode(localcs, servercs);
288154177Sharti	if (error == 0)
289154177Sharti		return 0;
290154177Sharti	smb_error("can't initialize iconv support (%s:%s)",
291165419Ssyrinx	    error, localcs, servercs);
292165419Ssyrinx	localcs[0] = 0;
293165419Ssyrinx	servercs[0] = 0;
294165419Ssyrinx	return error;
295165419Ssyrinx}
296165419Ssyrinx
297210503Ssyrinxint
298210503Ssyrinxsmb_ctx_setserver(struct smb_ctx *ctx, const char *name)
299210503Ssyrinx{
300210503Ssyrinx	if (strlen(name) > SMB_MAXSRVNAMELEN) {
301210503Ssyrinx		smb_error("server name '%s' too long", 0, name);
302210503Ssyrinx		return ENAMETOOLONG;
303	}
304	nls_str_upper(ctx->ct_ssn.ioc_srvname, name);
305	return 0;
306}
307
308int
309smb_ctx_setnbport(struct smb_ctx *ctx, int port)
310{
311	if (port < 1 || port > 0xffff)
312		return EINVAL;
313	ctx->ct_nb->nb_nmbtcpport = port;
314	return 0;
315}
316
317int
318smb_ctx_setsmbport(struct smb_ctx *ctx, int port)
319{
320	if (port < 1 || port > 0xffff)
321		return EINVAL;
322	ctx->ct_smbtcpport = port;
323	ctx->ct_nb->nb_smbtcpport = port;
324	return 0;
325}
326
327int
328smb_ctx_setuser(struct smb_ctx *ctx, const char *name)
329{
330	if (strlen(name) > SMB_MAXUSERNAMELEN) {
331		smb_error("user name '%s' too long", 0, name);
332		return ENAMETOOLONG;
333	}
334	nls_str_upper(ctx->ct_ssn.ioc_user, name);
335	return 0;
336}
337
338int
339smb_ctx_setworkgroup(struct smb_ctx *ctx, const char *name)
340{
341	if (strlen(name) > SMB_MAXUSERNAMELEN) {
342		smb_error("workgroup name '%s' too long", 0, name);
343		return ENAMETOOLONG;
344	}
345	nls_str_upper(ctx->ct_ssn.ioc_workgroup, name);
346	return 0;
347}
348
349int
350smb_ctx_setpassword(struct smb_ctx *ctx, const char *passwd)
351{
352	if (passwd == NULL)
353		return EINVAL;
354	if (strlen(passwd) > SMB_MAXPASSWORDLEN) {
355		smb_error("password too long", 0);
356		return ENAMETOOLONG;
357	}
358	if (strncmp(passwd, "$$1", 3) == 0)
359		smb_simpledecrypt(ctx->ct_ssn.ioc_password, passwd);
360	else
361		strcpy(ctx->ct_ssn.ioc_password, passwd);
362	strcpy(ctx->ct_sh.ioc_password, ctx->ct_ssn.ioc_password);
363	return 0;
364}
365
366int
367smb_ctx_setshare(struct smb_ctx *ctx, const char *share, int stype)
368{
369	if (strlen(share) > SMB_MAXSHARENAMELEN) {
370		smb_error("share name '%s' too long", 0, share);
371		return ENAMETOOLONG;
372	}
373	nls_str_upper(ctx->ct_sh.ioc_share, share);
374	if (share[0] != 0)
375		ctx->ct_parsedlevel = SMBL_SHARE;
376	ctx->ct_sh.ioc_stype = stype;
377	return 0;
378}
379
380int
381smb_ctx_setsrvaddr(struct smb_ctx *ctx, const char *addr)
382{
383	if (addr == NULL || addr[0] == 0)
384		return EINVAL;
385	if (ctx->ct_srvaddr)
386		free(ctx->ct_srvaddr);
387	if ((ctx->ct_srvaddr = strdup(addr)) == NULL)
388		return ENOMEM;
389	return 0;
390}
391
392static int
393smb_parse_owner(char *pair, uid_t *uid, gid_t *gid)
394{
395	struct group *gr;
396	struct passwd *pw;
397	char *cp;
398
399	cp = strchr(pair, ':');
400	if (cp) {
401		*cp++ = '\0';
402		if (*cp) {
403			gr = getgrnam(cp);
404			if (gr) {
405				*gid = gr->gr_gid;
406			} else
407				smb_error("Invalid group name %s, ignored",
408				    0, cp);
409		}
410	}
411	if (*pair) {
412		pw = getpwnam(pair);
413		if (pw) {
414			*uid = pw->pw_uid;
415		} else
416			smb_error("Invalid user name %s, ignored", 0, pair);
417	}
418	endpwent();
419	return 0;
420}
421
422int
423smb_ctx_opt(struct smb_ctx *ctx, int opt, const char *arg)
424{
425	int error = 0;
426	char *p, *cp;
427
428	switch(opt) {
429	    case 'U':
430		break;
431	    case 'I':
432		error = smb_ctx_setsrvaddr(ctx, arg);
433		break;
434	    case 'M':
435		ctx->ct_ssn.ioc_rights = strtol(arg, &cp, 8);
436		if (*cp == '/') {
437			ctx->ct_sh.ioc_rights = strtol(cp + 1, &cp, 8);
438			ctx->ct_flags |= SMBCF_SRIGHTS;
439		}
440		break;
441	    case 'N':
442		ctx->ct_flags |= SMBCF_NOPWD;
443		break;
444	    case 'O':
445		p = strdup(arg);
446		cp = strchr(p, '/');
447		if (cp) {
448			*cp++ = '\0';
449			error = smb_parse_owner(cp, &ctx->ct_sh.ioc_owner,
450			    &ctx->ct_sh.ioc_group);
451		}
452		if (*p && error == 0) {
453			error = smb_parse_owner(p, &ctx->ct_ssn.ioc_owner,
454			    &ctx->ct_ssn.ioc_group);
455		}
456		free(p);
457		break;
458	    case 'P':
459/*		ctx->ct_ssn.ioc_opt |= SMBCOPT_PERMANENT;*/
460		break;
461	    case 'R':
462		ctx->ct_ssn.ioc_retrycount = atoi(arg);
463		break;
464	    case 'T':
465		ctx->ct_ssn.ioc_timeout = atoi(arg);
466		break;
467	    case 'W':
468		error = smb_ctx_setworkgroup(ctx, arg);
469		break;
470	}
471	return error;
472}
473
474#if 0
475static void
476smb_hexdump(const u_char *buf, int len) {
477	int ofs = 0;
478
479	while (len--) {
480		if (ofs % 16 == 0)
481			printf("\n%02X: ", ofs);
482		printf("%02x ", *buf++);
483		ofs++;
484	}
485	printf("\n");
486}
487#endif
488
489
490static int
491smb_addiconvtbl(const char *to, const char *from, const u_char *tbl)
492{
493	int error;
494
495	error = kiconv_add_xlat_table(to, from, tbl);
496	if (error && error != EEXIST) {
497		smb_error("can not setup kernel iconv table (%s:%s)", error,
498		    from, to);
499		return error;
500	}
501	return 0;
502}
503
504/*
505 * Verify context before connect operation(s),
506 * lookup specified server and try to fill all forgotten fields.
507 */
508int
509smb_ctx_resolve(struct smb_ctx *ctx)
510{
511	struct smbioc_ossn *ssn = &ctx->ct_ssn;
512	struct smbioc_oshare *sh = &ctx->ct_sh;
513	struct nb_name nn;
514	struct sockaddr *sap;
515	struct sockaddr_nb *salocal, *saserver;
516	char *cp;
517	int error = 0;
518
519	ctx->ct_flags &= ~SMBCF_RESOLVED;
520	if (ssn->ioc_srvname[0] == 0) {
521		smb_error("no server name specified", 0);
522		return EINVAL;
523	}
524	if (ssn->ioc_user[0] == 0) {
525		smb_error("no user name specified for server %s",
526		    0, ssn->ioc_srvname);
527		return EINVAL;
528	}
529	if (ctx->ct_minlevel >= SMBL_SHARE && sh->ioc_share[0] == 0) {
530		smb_error("no share name specified for %s@%s",
531		    0, ssn->ioc_user, ssn->ioc_srvname);
532		return EINVAL;
533	}
534	error = nb_ctx_resolve(ctx->ct_nb);
535	if (error)
536		return error;
537	if (ssn->ioc_localcs[0] == 0)
538		strcpy(ssn->ioc_localcs, "ISO8859-1");
539	error = smb_addiconvtbl("tolower", ssn->ioc_localcs, nls_lower);
540	if (error)
541		return error;
542	error = smb_addiconvtbl("toupper", ssn->ioc_localcs, nls_upper);
543	if (error)
544		return error;
545	if (ssn->ioc_servercs[0] != 0) {
546		error = kiconv_add_xlat16_cspairs
547			(ssn->ioc_servercs, ssn->ioc_localcs);
548		if (error) return error;
549	}
550	if (ctx->ct_srvaddr) {
551		error = nb_resolvehost_in(ctx->ct_srvaddr, &sap, ctx->ct_smbtcpport);
552	} else {
553		error = nbns_resolvename(ssn->ioc_srvname, ctx->ct_nb, &sap);
554	}
555	if (error) {
556		smb_error("can't get server address", error);
557		return error;
558	}
559	nn.nn_scope = ctx->ct_nb->nb_scope;
560	nn.nn_type = NBT_SERVER;
561	strcpy(nn.nn_name, ssn->ioc_srvname);
562	error = nb_sockaddr(sap, &nn, &saserver);
563	nb_snbfree(sap);
564	if (error) {
565		smb_error("can't allocate server address", error);
566		return error;
567	}
568	ssn->ioc_server = (struct sockaddr*)saserver;
569	if (ctx->ct_locname[0] == 0) {
570		error = nb_getlocalname(ctx->ct_locname);
571		if (error) {
572			smb_error("can't get local name", error);
573			return error;
574		}
575		nls_str_upper(ctx->ct_locname, ctx->ct_locname);
576	}
577	strcpy(nn.nn_name, ctx->ct_locname);
578	nn.nn_type = NBT_WKSTA;
579	nn.nn_scope = ctx->ct_nb->nb_scope;
580	error = nb_sockaddr(NULL, &nn, &salocal);
581	if (error) {
582		nb_snbfree((struct sockaddr*)saserver);
583		smb_error("can't allocate local address", error);
584		return error;
585	}
586	ssn->ioc_local = (struct sockaddr*)salocal;
587	ssn->ioc_lolen = salocal->snb_len;
588	ssn->ioc_svlen = saserver->snb_len;
589	if (ssn->ioc_password[0] == 0 && (ctx->ct_flags & SMBCF_NOPWD) == 0) {
590		cp = getpass("Password:");
591		error = smb_ctx_setpassword(ctx, cp);
592		if (error)
593			return error;
594	}
595	ctx->ct_flags |= SMBCF_RESOLVED;
596	return 0;
597}
598
599static int
600smb_ctx_gethandle(struct smb_ctx *ctx)
601{
602	int fd, i;
603	char buf[20];
604
605	/*
606	 * First, try to open as cloned device
607	 */
608	fd = open("/dev/"NSMB_NAME, O_RDWR);
609	if (fd >= 0) {
610		ctx->ct_fd = fd;
611		return 0;
612	}
613	/*
614	 * well, no clone capabilities available - we have to scan
615	 * all devices in order to get free one
616	 */
617	 for (i = 0; i < 1024; i++) {
618	         snprintf(buf, sizeof(buf), "/dev/%s%d", NSMB_NAME, i);
619		 fd = open(buf, O_RDWR);
620		 if (fd >= 0) {
621			ctx->ct_fd = fd;
622			return 0;
623		 }
624	 }
625	 /*
626	  * This is a compatibility with old /dev/net/nsmb device
627	  */
628	 for (i = 0; i < 1024; i++) {
629	         snprintf(buf, sizeof(buf), "/dev/net/%s%d", NSMB_NAME, i);
630		 fd = open(buf, O_RDWR);
631		 if (fd >= 0) {
632			ctx->ct_fd = fd;
633			return 0;
634		 }
635		 if (errno == ENOENT)
636		         return ENOENT;
637	 }
638	 return ENOENT;
639}
640
641int
642smb_ctx_lookup(struct smb_ctx *ctx, int level, int flags)
643{
644	struct smbioc_lookup rq;
645	int error;
646
647	if ((ctx->ct_flags & SMBCF_RESOLVED) == 0) {
648		smb_error("smb_ctx_lookup() data is not resolved", 0);
649		return EINVAL;
650	}
651	if (ctx->ct_fd != -1) {
652		close(ctx->ct_fd);
653		ctx->ct_fd = -1;
654	}
655	error = smb_ctx_gethandle(ctx);
656	if (error) {
657		smb_error("can't get handle to requester (no /dev/"NSMB_NAME"* device)", 0);
658		return EINVAL;
659	}
660	bzero(&rq, sizeof(rq));
661	bcopy(&ctx->ct_ssn, &rq.ioc_ssn, sizeof(struct smbioc_ossn));
662	bcopy(&ctx->ct_sh, &rq.ioc_sh, sizeof(struct smbioc_oshare));
663	rq.ioc_flags = flags;
664	rq.ioc_level = level;
665	if (ioctl(ctx->ct_fd, SMBIOC_LOOKUP, &rq) == -1) {
666		error = errno;
667		if (flags & SMBLK_CREATE)
668			smb_error("unable to open connection", error);
669		return error;
670	}
671	return 0;
672}
673
674int
675smb_ctx_login(struct smb_ctx *ctx)
676{
677	struct smbioc_ossn *ssn = &ctx->ct_ssn;
678	struct smbioc_oshare *sh = &ctx->ct_sh;
679	int error;
680
681	if ((ctx->ct_flags & SMBCF_RESOLVED) == 0) {
682		smb_error("smb_ctx_resolve() should be called first", 0);
683		return EINVAL;
684	}
685	if (ctx->ct_fd != -1) {
686		close(ctx->ct_fd);
687		ctx->ct_fd = -1;
688	}
689	error = smb_ctx_gethandle(ctx);
690	if (error) {
691		smb_error("can't get handle to requester", 0);
692		return EINVAL;
693	}
694	if (ioctl(ctx->ct_fd, SMBIOC_OPENSESSION, ssn) == -1) {
695		error = errno;
696		smb_error("can't open session to server %s", error, ssn->ioc_srvname);
697		return error;
698	}
699	if (sh->ioc_share[0] == 0)
700		return 0;
701	if (ioctl(ctx->ct_fd, SMBIOC_OPENSHARE, sh) == -1) {
702		error = errno;
703		smb_error("can't connect to share //%s/%s", error,
704		    ssn->ioc_srvname, sh->ioc_share);
705		return error;
706	}
707	return 0;
708}
709
710int
711smb_ctx_setflags(struct smb_ctx *ctx, int level, int mask, int flags)
712{
713	struct smbioc_flags fl;
714
715	if (ctx->ct_fd == -1)
716		return EINVAL;
717	fl.ioc_level = level;
718	fl.ioc_mask = mask;
719	fl.ioc_flags = flags;
720	if (ioctl(ctx->ct_fd, SMBIOC_SETFLAGS, &fl) == -1)
721		return errno;
722	return 0;
723}
724
725/*
726 * level values:
727 * 0 - default
728 * 1 - server
729 * 2 - server:user
730 * 3 - server:user:share
731 */
732static int
733smb_ctx_readrcsection(struct smb_ctx *ctx, const char *sname, int level)
734{
735	char *p;
736	int error;
737
738	if (level >= 0) {
739		rc_getstringptr(smb_rc, sname, "charsets", &p);
740		if (p) {
741			error = smb_ctx_setcharset(ctx, p);
742			if (error)
743				smb_error("charset specification in the section '%s' ignored", error, sname);
744		}
745	}
746	if (level <= 1) {
747		rc_getint(smb_rc, sname, "timeout", &ctx->ct_ssn.ioc_timeout);
748		rc_getint(smb_rc, sname, "retry_count", &ctx->ct_ssn.ioc_retrycount);
749	}
750	if (level == 1) {
751		rc_getstringptr(smb_rc, sname, "addr", &p);
752		if (p) {
753			error = smb_ctx_setsrvaddr(ctx, p);
754			if (error) {
755				smb_error("invalid address specified in the section %s", 0, sname);
756				return error;
757			}
758		}
759	}
760	if (level >= 2) {
761		rc_getstringptr(smb_rc, sname, "password", &p);
762		if (p)
763			smb_ctx_setpassword(ctx, p);
764	}
765	rc_getstringptr(smb_rc, sname, "workgroup", &p);
766	if (p)
767		smb_ctx_setworkgroup(ctx, p);
768	return 0;
769}
770
771/*
772 * read rc file as follows:
773 * 1. read [default] section
774 * 2. override with [server] section
775 * 3. override with [server:user:share] section
776 * Since abcence of rcfile is not fatal, silently ignore this fact.
777 * smb_rc file should be closed by caller.
778 */
779int
780smb_ctx_readrc(struct smb_ctx *ctx)
781{
782	char sname[SMB_MAXSRVNAMELEN + SMB_MAXUSERNAMELEN + SMB_MAXSHARENAMELEN + 4];
783/*	char *p;*/
784
785	if (smb_open_rcfile() != 0)
786		return 0;
787
788	if (ctx->ct_ssn.ioc_user[0] == 0 || ctx->ct_ssn.ioc_srvname[0] == 0)
789		return 0;
790
791	smb_ctx_readrcsection(ctx, "default", 0);
792	nb_ctx_readrcsection(smb_rc, ctx->ct_nb, "default", 0);
793	smb_ctx_readrcsection(ctx, ctx->ct_ssn.ioc_srvname, 1);
794	nb_ctx_readrcsection(smb_rc, ctx->ct_nb, ctx->ct_ssn.ioc_srvname, 1);
795	/*
796	 * SERVER:USER parameters
797	 */
798	snprintf(sname, sizeof(sname), "%s:%s", ctx->ct_ssn.ioc_srvname,
799	    ctx->ct_ssn.ioc_user);
800	smb_ctx_readrcsection(ctx, sname, 2);
801
802	if (ctx->ct_sh.ioc_share[0] != 0) {
803		/*
804		 * SERVER:USER:SHARE parameters
805	         */
806		snprintf(sname, sizeof(sname), "%s:%s:%s", ctx->ct_ssn.ioc_srvname,
807		    ctx->ct_ssn.ioc_user, ctx->ct_sh.ioc_share);
808		smb_ctx_readrcsection(ctx, sname, 3);
809	}
810	return 0;
811}
812
813