ctx.c revision 95268
1/*
2 * Copyright (c) 2000-2002, 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: ctx.c,v 1.24 2002/04/13 14:35:28 bp Exp $
33 */
34#include <sys/param.h>
35#include <sys/sysctl.h>
36#include <sys/ioctl.h>
37#include <sys/time.h>
38#include <sys/mount.h>
39#include <fcntl.h>
40#include <ctype.h>
41#include <errno.h>
42#include <stdio.h>
43#include <string.h>
44#include <stdlib.h>
45#include <pwd.h>
46#include <grp.h>
47#include <unistd.h>
48#include <sys/iconv.h>
49
50#define NB_NEEDRESOLVER
51
52#include <netsmb/smb_lib.h>
53#include <netsmb/netbios.h>
54#include <netsmb/nb_lib.h>
55#include <netsmb/smb_conn.h>
56#include <cflib.h>
57
58/*
59 * Prescan command line for [-U user] argument
60 * and fill context with defaults
61 */
62int
63smb_ctx_init(struct smb_ctx *ctx, int argc, char *argv[],
64	int minlevel, int maxlevel, int sharetype)
65{
66	int  opt, error = 0;
67	uid_t euid;
68	const char *arg, *cp;
69	struct passwd *pwd;
70
71	bzero(ctx,sizeof(*ctx));
72	error = nb_ctx_create(&ctx->ct_nb);
73	if (error)
74		return error;
75	ctx->ct_fd = -1;
76	ctx->ct_parsedlevel = SMBL_NONE;
77	ctx->ct_minlevel = minlevel;
78	ctx->ct_maxlevel = maxlevel;
79
80	ctx->ct_ssn.ioc_opt = SMBVOPT_CREATE;
81	ctx->ct_ssn.ioc_timeout = 15;
82	ctx->ct_ssn.ioc_retrycount = 4;
83	ctx->ct_ssn.ioc_owner = SMBM_ANY_OWNER;
84	ctx->ct_ssn.ioc_group = SMBM_ANY_GROUP;
85	ctx->ct_ssn.ioc_mode = SMBM_EXEC;
86	ctx->ct_ssn.ioc_rights = SMBM_DEFAULT;
87
88	ctx->ct_sh.ioc_opt = SMBVOPT_CREATE;
89	ctx->ct_sh.ioc_owner = SMBM_ANY_OWNER;
90	ctx->ct_sh.ioc_group = SMBM_ANY_GROUP;
91	ctx->ct_sh.ioc_mode = SMBM_EXEC;
92	ctx->ct_sh.ioc_rights = SMBM_DEFAULT;
93	ctx->ct_sh.ioc_owner = SMBM_ANY_OWNER;
94	ctx->ct_sh.ioc_group = SMBM_ANY_GROUP;
95
96	nb_ctx_setscope(ctx->ct_nb, "");
97	euid = geteuid();
98	if ((pwd = getpwuid(euid)) != NULL) {
99		smb_ctx_setuser(ctx, pwd->pw_name);
100		endpwent();
101	} else if (euid == 0)
102		smb_ctx_setuser(ctx, "root");
103	else
104		return 0;
105	if (argv == NULL)
106		return 0;
107	for (opt = 1; opt < argc; opt++) {
108		cp = argv[opt];
109		if (strncmp(cp, "//", 2) != 0)
110			continue;
111		error = smb_ctx_parseunc(ctx, cp, sharetype, (const char**)&cp);
112		if (error)
113			return error;
114		ctx->ct_uncnext = cp;
115		break;
116	}
117	while (error == 0 && (opt = cf_getopt(argc, argv, ":E:L:U:")) != -1) {
118		arg = cf_optarg;
119		switch (opt) {
120		    case 'E':
121			error = smb_ctx_setcharset(ctx, arg);
122			if (error)
123				return error;
124			break;
125		    case 'L':
126			error = nls_setlocale(optarg);
127			if (error)
128				break;
129			break;
130		    case 'U':
131			error = smb_ctx_setuser(ctx, arg);
132			break;
133		}
134	}
135	cf_optind = cf_optreset = 1;
136	return error;
137}
138
139void
140smb_ctx_done(struct smb_ctx *ctx)
141{
142	if (ctx->ct_ssn.ioc_server)
143		nb_snbfree(ctx->ct_ssn.ioc_server);
144	if (ctx->ct_ssn.ioc_local)
145		nb_snbfree(ctx->ct_ssn.ioc_local);
146	if (ctx->ct_srvaddr)
147		free(ctx->ct_srvaddr);
148	if (ctx->ct_nb)
149		nb_ctx_done(ctx->ct_nb);
150}
151
152static int
153getsubstring(const char *p, u_char sep, char *dest, int maxlen, const char **next)
154{
155	int len;
156
157	maxlen--;
158	for (len = 0; len < maxlen && *p != sep; p++, len++, dest++) {
159		if (*p == 0)
160			return EINVAL;
161		*dest = *p;
162	}
163	*dest = 0;
164	*next = *p ? p + 1 : p;
165	return 0;
166}
167
168/*
169 * Here we expect something like "[proto:]//[user@]host[/share][/path]"
170 */
171int
172smb_ctx_parseunc(struct smb_ctx *ctx, const char *unc, int sharetype,
173	const char **next)
174{
175	const char *p = unc;
176	char *p1;
177	char tmp[1024];
178	int error ;
179
180	ctx->ct_parsedlevel = SMBL_NONE;
181	if (*p++ != '/' || *p++ != '/') {
182		smb_error("UNC should start with '//'", 0);
183		return EINVAL;
184	}
185	p1 = tmp;
186	error = getsubstring(p, '@', p1, sizeof(tmp), &p);
187	if (!error) {
188		if (ctx->ct_maxlevel < SMBL_VC) {
189			smb_error("no user name required", 0);
190			return EINVAL;
191		}
192		if (*p1 == 0) {
193			smb_error("empty user name", 0);
194			return EINVAL;
195		}
196		error = smb_ctx_setuser(ctx, tmp);
197		if (error)
198			return error;
199		ctx->ct_parsedlevel = SMBL_VC;
200	}
201	error = getsubstring(p, '/', p1, sizeof(tmp), &p);
202	if (error) {
203		error = getsubstring(p, '\0', p1, sizeof(tmp), &p);
204		if (error) {
205			smb_error("no server name found", 0);
206			return error;
207		}
208	}
209	if (*p1 == 0) {
210		smb_error("empty server name", 0);
211		return EINVAL;
212	}
213	error = smb_ctx_setserver(ctx, tmp);
214	if (error)
215		return error;
216	if (sharetype == SMB_ST_NONE) {
217		*next = p;
218		return 0;
219	}
220	if (*p != 0 && ctx->ct_maxlevel < SMBL_SHARE) {
221		smb_error("no share name required", 0);
222		return EINVAL;
223	}
224	error = getsubstring(p, '/', p1, sizeof(tmp), &p);
225	if (error) {
226		error = getsubstring(p, '\0', p1, sizeof(tmp), &p);
227		if (error) {
228			smb_error("unexpected end of line", 0);
229			return error;
230		}
231	}
232	if (*p1 == 0 && ctx->ct_minlevel >= SMBL_SHARE) {
233		smb_error("empty share name", 0);
234		return EINVAL;
235	}
236	*next = p;
237	if (*p1 == 0)
238		return 0;
239	error = smb_ctx_setshare(ctx, p1, sharetype);
240	return error;
241}
242
243int
244smb_ctx_setcharset(struct smb_ctx *ctx, const char *arg)
245{
246	char *cp, *servercs, *localcs;
247	int cslen = sizeof(ctx->ct_ssn.ioc_localcs);
248	int scslen, lcslen, error;
249
250	cp = strchr(arg, ':');
251	lcslen = cp ? (cp - arg) : 0;
252	if (lcslen == 0 || lcslen >= cslen) {
253		smb_error("invalid local charset specification (%s)", 0, arg);
254		return EINVAL;
255	}
256	scslen = (size_t)strlen(++cp);
257	if (scslen == 0 || scslen >= cslen) {
258		smb_error("invalid server charset specification (%s)", 0, arg);
259		return EINVAL;
260	}
261	localcs = memcpy(ctx->ct_ssn.ioc_localcs, arg, lcslen);
262	localcs[lcslen] = 0;
263	servercs = strcpy(ctx->ct_ssn.ioc_servercs, cp);
264	error = nls_setrecode(localcs, servercs);
265	if (error == 0)
266		return 0;
267	smb_error("can't initialize iconv support (%s:%s)",
268	    error, localcs, servercs);
269	localcs[0] = 0;
270	servercs[0] = 0;
271	return error;
272}
273
274int
275smb_ctx_setserver(struct smb_ctx *ctx, const char *name)
276{
277	if (strlen(name) >= SMB_MAXSRVNAMELEN) {
278		smb_error("server name '%s' too long", 0, name);
279		return ENAMETOOLONG;
280	}
281	nls_str_upper(ctx->ct_ssn.ioc_srvname, name);
282	return 0;
283}
284
285int
286smb_ctx_setuser(struct smb_ctx *ctx, const char *name)
287{
288	if (strlen(name) >= SMB_MAXUSERNAMELEN) {
289		smb_error("user name '%s' too long", 0, name);
290		return ENAMETOOLONG;
291	}
292	nls_str_upper(ctx->ct_ssn.ioc_user, name);
293	return 0;
294}
295
296int
297smb_ctx_setworkgroup(struct smb_ctx *ctx, const char *name)
298{
299	if (strlen(name) >= SMB_MAXUSERNAMELEN) {
300		smb_error("workgroup name '%s' too long", 0, name);
301		return ENAMETOOLONG;
302	}
303	nls_str_upper(ctx->ct_ssn.ioc_workgroup, name);
304	return 0;
305}
306
307int
308smb_ctx_setpassword(struct smb_ctx *ctx, const char *passwd)
309{
310	if (passwd == NULL)
311		return EINVAL;
312	if (strlen(passwd) >= SMB_MAXPASSWORDLEN) {
313		smb_error("password too long", 0);
314		return ENAMETOOLONG;
315	}
316	if (strncmp(passwd, "$$1", 3) == 0)
317		smb_simpledecrypt(ctx->ct_ssn.ioc_password, passwd);
318	else
319		strcpy(ctx->ct_ssn.ioc_password, passwd);
320	strcpy(ctx->ct_sh.ioc_password, ctx->ct_ssn.ioc_password);
321	return 0;
322}
323
324int
325smb_ctx_setshare(struct smb_ctx *ctx, const char *share, int stype)
326{
327	if (strlen(share) >= SMB_MAXSHARENAMELEN) {
328		smb_error("share name '%s' too long", 0, share);
329		return ENAMETOOLONG;
330	}
331	nls_str_upper(ctx->ct_sh.ioc_share, share);
332	if (share[0] != 0)
333		ctx->ct_parsedlevel = SMBL_SHARE;
334	ctx->ct_sh.ioc_stype = stype;
335	return 0;
336}
337
338int
339smb_ctx_setsrvaddr(struct smb_ctx *ctx, const char *addr)
340{
341	if (addr == NULL || addr[0] == 0)
342		return EINVAL;
343	if (ctx->ct_srvaddr)
344		free(ctx->ct_srvaddr);
345	if ((ctx->ct_srvaddr = strdup(addr)) == NULL)
346		return ENOMEM;
347	return 0;
348}
349
350static int
351smb_parse_owner(char *pair, uid_t *uid, gid_t *gid)
352{
353	struct group *gr;
354	struct passwd *pw;
355	char *cp;
356
357	cp = strchr(pair, ':');
358	if (cp) {
359		*cp++ = '\0';
360		if (*cp) {
361			gr = getgrnam(cp);
362			if (gr) {
363				*gid = gr->gr_gid;
364			} else
365				smb_error("Invalid group name %s, ignored",
366				    0, cp);
367		}
368	}
369	if (*pair) {
370		pw = getpwnam(pair);
371		if (pw) {
372			*uid = pw->pw_uid;
373		} else
374			smb_error("Invalid user name %s, ignored", 0, pair);
375	}
376	endpwent();
377	return 0;
378}
379
380int
381smb_ctx_opt(struct smb_ctx *ctx, int opt, const char *arg)
382{
383	int error = 0;
384	char *p, *cp;
385
386	switch(opt) {
387	    case 'U':
388		break;
389	    case 'I':
390		error = smb_ctx_setsrvaddr(ctx, arg);
391		break;
392	    case 'M':
393		ctx->ct_ssn.ioc_rights = strtol(arg, &cp, 8);
394		if (*cp == '/') {
395			ctx->ct_sh.ioc_rights = strtol(cp + 1, &cp, 8);
396			ctx->ct_flags |= SMBCF_SRIGHTS;
397		}
398		break;
399	    case 'N':
400		ctx->ct_flags |= SMBCF_NOPWD;
401		break;
402	    case 'O':
403		p = strdup(arg);
404		cp = strchr(p, '/');
405		if (cp) {
406			*cp++ = '\0';
407			error = smb_parse_owner(cp, &ctx->ct_sh.ioc_owner,
408			    &ctx->ct_sh.ioc_group);
409		}
410		if (*p && error == 0) {
411			error = smb_parse_owner(cp, &ctx->ct_ssn.ioc_owner,
412			    &ctx->ct_ssn.ioc_group);
413		}
414		free(p);
415		break;
416	    case 'P':
417/*		ctx->ct_ssn.ioc_opt |= SMBCOPT_PERMANENT;*/
418		break;
419	    case 'R':
420		ctx->ct_ssn.ioc_retrycount = atoi(arg);
421		break;
422	    case 'T':
423		ctx->ct_ssn.ioc_timeout = atoi(arg);
424		break;
425	    case 'W':
426		error = smb_ctx_setworkgroup(ctx, arg);
427		break;
428	}
429	return error;
430}
431
432#if 0
433static void
434smb_hexdump(const u_char *buf, int len) {
435	int ofs = 0;
436
437	while (len--) {
438		if (ofs % 16 == 0)
439			printf("\n%02X: ", ofs);
440		printf("%02x ", *buf++);
441		ofs++;
442	}
443	printf("\n");
444}
445#endif
446
447
448static int
449smb_addiconvtbl(const char *to, const char *from, const u_char *tbl)
450{
451	int error;
452
453	error = kiconv_add_xlat_table(to, from, tbl);
454	if (error && error != EEXIST) {
455		smb_error("can not setup kernel iconv table (%s:%s)", error,
456		    from, to);
457		return error;
458	}
459	return 0;
460}
461
462/*
463 * Verify context before connect operation(s),
464 * lookup specified server and try to fill all forgotten fields.
465 */
466int
467smb_ctx_resolve(struct smb_ctx *ctx)
468{
469	struct smbioc_ossn *ssn = &ctx->ct_ssn;
470	struct smbioc_oshare *sh = &ctx->ct_sh;
471	struct nb_name nn;
472	struct sockaddr *sap;
473	struct sockaddr_nb *salocal, *saserver;
474	char *cp;
475	u_char cstbl[256];
476	u_int i;
477	int error = 0;
478
479	ctx->ct_flags &= ~SMBCF_RESOLVED;
480	if (ssn->ioc_srvname[0] == 0) {
481		smb_error("no server name specified", 0);
482		return EINVAL;
483	}
484	if (ssn->ioc_user[0] == 0) {
485		smb_error("no user name specified for server %s",
486		    0, ssn->ioc_srvname);
487		return EINVAL;
488	}
489	if (ctx->ct_minlevel >= SMBL_SHARE && sh->ioc_share[0] == 0) {
490		smb_error("no share name specified for %s@%s",
491		    0, ssn->ioc_user, ssn->ioc_srvname);
492		return EINVAL;
493	}
494	error = nb_ctx_resolve(ctx->ct_nb);
495	if (error)
496		return error;
497	if (ssn->ioc_localcs[0] == 0)
498		strcpy(ssn->ioc_localcs, "default");	/* XXX: locale name ? */
499	error = smb_addiconvtbl("tolower", ssn->ioc_localcs, nls_lower);
500	if (error)
501		return error;
502	error = smb_addiconvtbl("toupper", ssn->ioc_localcs, nls_upper);
503	if (error)
504		return error;
505	if (ssn->ioc_servercs[0] != 0) {
506		for(i = 0; i < sizeof(cstbl); i++)
507			cstbl[i] = i;
508		nls_mem_toext(cstbl, cstbl, sizeof(cstbl));
509		error = smb_addiconvtbl(ssn->ioc_servercs, ssn->ioc_localcs, cstbl);
510		if (error)
511			return error;
512		for(i = 0; i < sizeof(cstbl); i++)
513			cstbl[i] = i;
514		nls_mem_toloc(cstbl, cstbl, sizeof(cstbl));
515		error = smb_addiconvtbl(ssn->ioc_localcs, ssn->ioc_servercs, cstbl);
516		if (error)
517			return error;
518	}
519	if (ctx->ct_srvaddr) {
520		error = nb_resolvehost_in(ctx->ct_srvaddr, &sap);
521	} else {
522		error = nbns_resolvename(ssn->ioc_srvname, ctx->ct_nb, &sap);
523	}
524	if (error) {
525		smb_error("can't get server address", error);
526		return error;
527	}
528	nn.nn_scope = ctx->ct_nb->nb_scope;
529	nn.nn_type = NBT_SERVER;
530	strcpy(nn.nn_name, ssn->ioc_srvname);
531	error = nb_sockaddr(sap, &nn, &saserver);
532	nb_snbfree(sap);
533	if (error) {
534		smb_error("can't allocate server address", error);
535		return error;
536	}
537	ssn->ioc_server = (struct sockaddr*)saserver;
538	if (ctx->ct_locname[0] == 0) {
539		error = nb_getlocalname(ctx->ct_locname);
540		if (error) {
541			smb_error("can't get local name", error);
542			return error;
543		}
544		nls_str_upper(ctx->ct_locname, ctx->ct_locname);
545	}
546	strcpy(nn.nn_name, ctx->ct_locname);
547	nn.nn_type = NBT_WKSTA;
548	nn.nn_scope = ctx->ct_nb->nb_scope;
549	error = nb_sockaddr(NULL, &nn, &salocal);
550	if (error) {
551		nb_snbfree((struct sockaddr*)saserver);
552		smb_error("can't allocate local address", error);
553		return error;
554	}
555	ssn->ioc_local = (struct sockaddr*)salocal;
556	ssn->ioc_lolen = salocal->snb_len;
557	ssn->ioc_svlen = saserver->snb_len;
558	if (ssn->ioc_password[0] == 0 && (ctx->ct_flags & SMBCF_NOPWD) == 0) {
559		cp = getpass("Password:");
560		error = smb_ctx_setpassword(ctx, cp);
561		if (error)
562			return error;
563	}
564	ctx->ct_flags |= SMBCF_RESOLVED;
565	return 0;
566}
567
568static int
569smb_ctx_gethandle(struct smb_ctx *ctx)
570{
571	int fd, i;
572	char buf[20];
573
574	/*
575	 * First, try to open as cloned device
576	 */
577	fd = open("/dev/"NSMB_NAME, O_RDWR);
578	if (fd >= 0) {
579		ctx->ct_fd = fd;
580		return 0;
581	}
582	/*
583	 * well, no clone capabilities available - we have to scan
584	 * all devices in order to get free one
585	 */
586	 for (i = 0; i < 1024; i++) {
587	         snprintf(buf, sizeof(buf), "/dev/%s%d", NSMB_NAME, i);
588		 fd = open(buf, O_RDWR);
589		 if (fd >= 0) {
590			ctx->ct_fd = fd;
591			return 0;
592		 }
593	 }
594	 /*
595	  * This is a compatibility with old /dev/net/nsmb device
596	  */
597	 for (i = 0; i < 1024; i++) {
598	         snprintf(buf, sizeof(buf), "/dev/net/%s%d", NSMB_NAME, i);
599		 fd = open(buf, O_RDWR);
600		 if (fd >= 0) {
601			ctx->ct_fd = fd;
602			return 0;
603		 }
604		 if (errno == ENOENT)
605		         return ENOENT;
606	 }
607	 return ENOENT;
608}
609
610int
611smb_ctx_lookup(struct smb_ctx *ctx, int level, int flags)
612{
613	struct smbioc_lookup rq;
614	int error;
615
616	if ((ctx->ct_flags & SMBCF_RESOLVED) == 0) {
617		smb_error("smb_ctx_lookup() data is not resolved", 0);
618		return EINVAL;
619	}
620	if (ctx->ct_fd != -1) {
621		close(ctx->ct_fd);
622		ctx->ct_fd = -1;
623	}
624	error = smb_ctx_gethandle(ctx);
625	if (error) {
626		smb_error("can't get handle to requester (no /dev/"NSMB_NAME"* device)", 0);
627		return EINVAL;
628	}
629	bzero(&rq, sizeof(rq));
630	bcopy(&ctx->ct_ssn, &rq.ioc_ssn, sizeof(struct smbioc_ossn));
631	bcopy(&ctx->ct_sh, &rq.ioc_sh, sizeof(struct smbioc_oshare));
632	rq.ioc_flags = flags;
633	rq.ioc_level = level;
634	if (ioctl(ctx->ct_fd, SMBIOC_LOOKUP, &rq) == -1) {
635		error = errno;
636		if (flags & SMBLK_CREATE)
637			smb_error("unable to open connection", error);
638		return error;
639	}
640	return 0;
641}
642
643int
644smb_ctx_login(struct smb_ctx *ctx)
645{
646	struct smbioc_ossn *ssn = &ctx->ct_ssn;
647	struct smbioc_oshare *sh = &ctx->ct_sh;
648	int error;
649
650	if ((ctx->ct_flags & SMBCF_RESOLVED) == 0) {
651		smb_error("smb_ctx_resolve() should be called first", 0);
652		return EINVAL;
653	}
654	if (ctx->ct_fd != -1) {
655		close(ctx->ct_fd);
656		ctx->ct_fd = -1;
657	}
658	error = smb_ctx_gethandle(ctx);
659	if (error) {
660		smb_error("can't get handle to requester", 0);
661		return EINVAL;
662	}
663	if (ioctl(ctx->ct_fd, SMBIOC_OPENSESSION, ssn) == -1) {
664		error = errno;
665		smb_error("can't open session to server %s", error, ssn->ioc_srvname);
666		return error;
667	}
668	if (sh->ioc_share[0] == 0)
669		return 0;
670	if (ioctl(ctx->ct_fd, SMBIOC_OPENSHARE, sh) == -1) {
671		error = errno;
672		smb_error("can't connect to share //%s/%s", error,
673		    ssn->ioc_srvname, sh->ioc_share);
674		return error;
675	}
676	return 0;
677}
678
679int
680smb_ctx_setflags(struct smb_ctx *ctx, int level, int mask, int flags)
681{
682	struct smbioc_flags fl;
683
684	if (ctx->ct_fd == -1)
685		return EINVAL;
686	fl.ioc_level = level;
687	fl.ioc_mask = mask;
688	fl.ioc_flags = flags;
689	if (ioctl(ctx->ct_fd, SMBIOC_SETFLAGS, &fl) == -1)
690		return errno;
691	return 0;
692}
693
694/*
695 * level values:
696 * 0 - default
697 * 1 - server
698 * 2 - server:user
699 * 3 - server:user:share
700 */
701static int
702smb_ctx_readrcsection(struct smb_ctx *ctx, const char *sname, int level)
703{
704	char *p;
705	int error;
706
707	if (level >= 0) {
708		rc_getstringptr(smb_rc, sname, "charsets", &p);
709		if (p) {
710			error = smb_ctx_setcharset(ctx, p);
711			if (error)
712				smb_error("charset specification in the section '%s' ignored", error, sname);
713		}
714	}
715	if (level <= 1) {
716		rc_getint(smb_rc, sname, "timeout", &ctx->ct_ssn.ioc_timeout);
717		rc_getint(smb_rc, sname, "retry_count", &ctx->ct_ssn.ioc_retrycount);
718	}
719	if (level == 1) {
720		rc_getstringptr(smb_rc, sname, "addr", &p);
721		if (p) {
722			error = smb_ctx_setsrvaddr(ctx, p);
723			if (error) {
724				smb_error("invalid address specified in the section %s", 0, sname);
725				return error;
726			}
727		}
728	}
729	if (level >= 2) {
730		rc_getstringptr(smb_rc, sname, "password", &p);
731		if (p)
732			smb_ctx_setpassword(ctx, p);
733	}
734	rc_getstringptr(smb_rc, sname, "workgroup", &p);
735	if (p)
736		smb_ctx_setworkgroup(ctx, p);
737	return 0;
738}
739
740/*
741 * read rc file as follows:
742 * 1. read [default] section
743 * 2. override with [server] section
744 * 3. override with [server:user:share] section
745 * Since abcence of rcfile is not fatal, silently ignore this fact.
746 * smb_rc file should be closed by caller.
747 */
748int
749smb_ctx_readrc(struct smb_ctx *ctx)
750{
751	char sname[SMB_MAXSRVNAMELEN + SMB_MAXUSERNAMELEN + SMB_MAXSHARENAMELEN + 4];
752/*	char *p;*/
753
754	if (smb_open_rcfile() != 0)
755		return 0;
756
757	if (ctx->ct_ssn.ioc_user[0] == 0 || ctx->ct_ssn.ioc_srvname[0] == 0)
758		return 0;
759
760	smb_ctx_readrcsection(ctx, "default", 0);
761	nb_ctx_readrcsection(smb_rc, ctx->ct_nb, "default", 0);
762	smb_ctx_readrcsection(ctx, ctx->ct_ssn.ioc_srvname, 1);
763	nb_ctx_readrcsection(smb_rc, ctx->ct_nb, ctx->ct_ssn.ioc_srvname, 1);
764	/*
765	 * SERVER:USER parameters
766	 */
767	snprintf(sname, sizeof(sname), "%s:%s", ctx->ct_ssn.ioc_srvname,
768	    ctx->ct_ssn.ioc_user);
769	smb_ctx_readrcsection(ctx, sname, 2);
770
771	if (ctx->ct_sh.ioc_share[0] != 0) {
772		/*
773		 * SERVER:USER:SHARE parameters
774	         */
775		snprintf(sname, sizeof(sname), "%s:%s:%s", ctx->ct_ssn.ioc_srvname,
776		    ctx->ct_ssn.ioc_user, ctx->ct_sh.ioc_share);
777		smb_ctx_readrcsection(ctx, sname, 3);
778	}
779	return 0;
780}
781
782