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