ncpl_conn.c revision 52626
1/*
2 * Copyright (c) 1999, 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 *  $FreeBSD: head/lib/libncp/ncpl_conn.c 52626 1999-10-29 12:59:59Z bp $
33 */
34
35/*
36 *
37 * Current scheme to create/open connection:
38 * 1. ncp_li_init() - lookup -S [-U] options in command line
39 * 2. ncp_li_init() - try to find existing connection
40 * 3. ncp_li_init() - if no server name and no accessible connections - bail out
41 * 4. This is connection candidate, read .rc file, override with command line
42 *    and go ahead
43 * Note: connection referenced only via ncp_login() call. Although it is
44 * possible to get connection handle in other way, it will be unwise to use
45 * it, since conn can be destroyed at any time.
46 *
47 */
48#include <sys/param.h>
49#include <sys/sysctl.h>
50#include <sys/ioctl.h>
51#include <sys/time.h>
52#include <sys/mount.h>
53#include <fcntl.h>
54#include <ctype.h>
55#include <errno.h>
56#include <stdio.h>
57#include <string.h>
58#include <stdlib.h>
59#include <pwd.h>
60#include <grp.h>
61#include <unistd.h>
62
63#include <netncp/ncp_lib.h>
64#include <netncp/ncp_rcfile.h>
65#include <nwfs/nwfs.h>
66
67static char *server_name; /* need a better way ! */
68
69
70
71int
72ncp_li_setserver(struct ncp_conn_loginfo *li, const char *arg) {
73	if (strlen(arg) >= NCP_BINDERY_NAME_LEN) {
74		ncp_error("server name '%s' too long", 0, arg);
75		return ENAMETOOLONG;
76	}
77	ncp_str_upper(strcpy(li->server, arg));
78	return 0;
79}
80
81int
82ncp_li_setuser(struct ncp_conn_loginfo *li, char *arg) {
83	if (arg && strlen(arg) >= NCP_BINDERY_NAME_LEN) {
84		ncp_error("user name '%s' too long", 0, arg);
85		return ENAMETOOLONG;
86	}
87	if (li->user)
88		free(li->user);
89	if (arg) {
90		li->user = strdup(arg);
91		if (li->user == NULL)
92			return ENOMEM;
93		ncp_str_upper(li->user);
94	} else
95		li->user = NULL;
96	return 0;
97}
98
99int
100ncp_li_setpassword(struct ncp_conn_loginfo *li, const char *passwd) {
101	if (passwd && strlen(passwd) >= 127) {
102		ncp_error("password too long", 0);
103		return ENAMETOOLONG;
104	}
105	if (li->password) {
106		bzero(li->password, strlen(li->password));
107		free(li->password);
108	}
109	if (passwd) {
110		li->password = strdup(passwd);
111		if (li->password == NULL)
112			return ENOMEM;
113	} else
114		li->password = NULL;
115	return 0;
116}
117/*
118 * Prescan command line for [-S server] [-U user] arguments
119 * and fill li structure with defaults
120 */
121int
122ncp_li_init(struct ncp_conn_loginfo *li, int argc, char *argv[]) {
123	int  opt, error = 0;
124	char *arg;
125
126	bzero(li,sizeof(*li));
127	li->timeout = 15;	/* these values should be large enough to handle */
128	li->retry_count = 4;	/* slow servers, even on ethernet */
129	li->access_mode = 0;
130	li->password = NULL;
131	li->sig_level = 1;
132	li->objtype = NCP_BINDERY_USER;
133	li->owner = NCP_DEFAULT_OWNER;
134	li->group = NCP_DEFAULT_GROUP;
135	server_name = NULL;
136	if (argv == NULL) return 0;
137	while (error == 0 && (opt = ncp_getopt(argc, argv, ":S:U:")) != -1) {
138		arg = ncp_optarg;
139		switch (opt) {
140		    case 'S':
141			error = ncp_li_setserver(li, arg);
142			break;
143		    case 'U':
144			error = ncp_li_setuser(li, arg);
145			break;
146		}
147	}
148	ncp_optind = ncp_optreset = 1;
149	return error;
150}
151
152void
153ncp_li_done(struct ncp_conn_loginfo *li) {
154	if (li->user)
155		free(li->user);
156	if (li->password)
157		free(li->password);
158}
159
160/*
161 * Lookup existing connection based on li structure, if connection
162 * found, it will be referenced. Otherwise full login sequence performed.
163 */
164int
165ncp_li_login(struct ncp_conn_loginfo *li, int *aconnid) {
166	int connHandle, error;
167
168	if ((error = ncp_conn_scan(li, &connHandle)) == 0) {
169		*aconnid = connHandle;
170		return 0;
171	}
172	error = ncp_connect(li, &connHandle);
173	if (error) return errno;
174	error = ncp_login(connHandle, li->user, li->objtype, li->password);
175	if (error) {
176		ncp_disconnect(connHandle);
177	} else
178		*aconnid = connHandle;
179	return error;
180}
181
182/*
183 * read rc file as follows:
184 * 1. read [server] section
185 * 2. override with [server:user] section
186 * Since abcence of rcfile is not a bug, silently ignore that fact.
187 * rcfile never closed to reduce number of open/close operations.
188 */
189int
190ncp_li_readrc(struct ncp_conn_loginfo *li) {
191	int i, val, error;
192	char uname[NCP_BINDERY_NAME_LEN*2+1];
193	char *sect = NULL, *p;
194
195	/*
196	 * if info from cmd line incomplete, try to find existing
197	 * connection and fill server/user from it.
198	 */
199	if (li->server[0] == 0 || li->user == NULL) {
200		int connHandle;
201		struct ncp_conn_stat cs;
202
203		if ((error = ncp_conn_scan(li, &connHandle)) != 0) {
204			ncp_error("no default connection found", errno);
205			return error;
206		}
207		ncp_conn_getinfo(connHandle, &cs);
208		ncp_li_setserver(li, cs.li.server);
209		ncp_li_setuser(li, cs.user);
210		ncp_li_setpassword(li, "");
211		ncp_disconnect(connHandle);
212	}
213	if (ncp_open_rcfile()) 	return 0;
214
215	for (i = 0; i < 2; i++) {
216		switch (i) {
217		    case 0:
218			sect = li->server;
219			break;
220		    case 1:
221			strcat(strcat(strcpy(uname,li->server),":"),li->user ? li->user : "default");
222			sect = uname;
223			break;
224		}
225		rc_getstringptr(ncp_rc, sect, "password", &p);
226		if (p)
227			ncp_li_setpassword(li, p);
228		rc_getint(ncp_rc,sect, "timeout", &li->timeout);
229		rc_getint(ncp_rc,sect, "retry_count", &li->retry_count);
230		rc_getint(ncp_rc,sect, "sig_level", &li->sig_level);
231		if (rc_getint(ncp_rc,sect,"access_mode",&val) == 0)
232			li->access_mode = val;
233		if(rc_getbool(ncp_rc,sect,"bindery",&val) == 0 && val) {
234			li->opt |= NCP_OPT_BIND;
235		}
236	}
237	return 0;
238}
239
240/*
241 * check for all uncompleted fields
242 */
243int
244ncp_li_check(struct ncp_conn_loginfo *li) {
245	int error = 0;
246	char *p;
247
248	do {
249		if (li->server[0] == 0) {
250			ncp_error("no server name specified", 0);
251			error = 1;
252			break;
253		}
254		error = ncp_find_fileserver(li,
255		    (server_name==NULL) ? AF_IPX : AF_INET, server_name);
256		if (error) {
257			ncp_error("can't find server %s", error, li->server);
258			break;
259		}
260		if (li->user == NULL || li->user[0] == 0) {
261			ncp_error("no user name specified for server %s",
262			    0, li->server);
263			error = 1;
264			break;
265		}
266		if (li->password == NULL) {
267			p = getpass("Netware password:");
268			error = ncp_li_setpassword(li, p) ? 1 : 0;
269		}
270	} while (0);
271	return error;
272}
273
274int
275ncp_conn_cnt(void) {
276	int error, cnt = 0, len = sizeof(cnt);
277
278#if __FreeBSD_version < 400001
279	error = sysctlbyname("net.ipx.ncp.conn_cnt", &cnt, &len, NULL, 0);
280#else
281	error = sysctlbyname("net.ncp.conn_cnt", &cnt, &len, NULL, 0);
282#endif
283	if (error) cnt = 0;
284	return cnt;
285}
286
287/*
288 * Find an existing connection and reference it
289 */
290int
291ncp_conn_find(char *server,char *user) {
292	struct ncp_conn_args ca;
293	int connid, error;
294
295	if (server == NULL && user == NULL) {
296		error = ncp_conn_scan(NULL,&connid);
297		if (error) return -2;
298		return connid;
299	}
300	if (server == NULL)
301		return -2;
302	ncp_str_upper(server);
303	if (user) ncp_str_upper(user);
304	bzero(&ca, sizeof(ca));
305	ncp_li_setserver(&ca, server);
306	ncp_li_setuser(&ca, user);
307	error = ncp_conn_scan(&ca,&connid);
308	if (error)
309		connid = -1;
310	return connid;
311}
312
313int
314ncp_li_arg(struct ncp_conn_loginfo *li, int opt, char *arg) {
315	int error = 0, sig_level;
316	char *p, *cp;
317	struct group *gr;
318	struct passwd *pw;
319
320	switch(opt) {
321	    case 'S': /* we already fill server/[user] pair */
322	    case 'U':
323		break;
324	    case 'A':
325		server_name = arg;
326		break;
327	    case 'B':
328		li->opt |= NCP_OPT_BIND;
329		break;
330	    case 'C':
331		li->opt |= NCP_OPT_NOUPCASEPASS;
332		break;
333	    case 'I':
334		sig_level = atoi(arg);
335		if (sig_level < 0 || sig_level > 3) {
336			ncp_error("invalid NCP signature level option `%s'\
337			    (must be a number between 0 and 3)", 0, arg);
338			error = 1;
339		}
340		li->sig_level = sig_level;
341		if (sig_level > 1) li->opt |= NCP_OPT_SIGN;
342		break;
343	    case 'M':
344		li->access_mode = strtol(arg, NULL, 8);
345		break;
346	    case 'N':
347		ncp_li_setpassword(li, "");
348		break;
349	    case 'O':
350		p = strdup(arg);
351		cp = strchr(p, ':');
352		if (cp) {
353			*cp++ = '\0';
354			if (*cp) {
355				gr = getgrnam(cp);
356				if (gr) {
357					li->group = gr->gr_gid;
358				} else
359					ncp_error("Invalid group name %s, ignored",
360					    0, cp);
361			}
362		}
363		if (*p) {
364			pw = getpwnam(p);
365			if (pw) {
366				li->owner = pw->pw_uid;
367			} else
368				ncp_error("Invalid user name %s, ignored", 0, p);
369		}
370		endpwent();
371		free(p);
372		break;
373	    case 'P':
374		li->opt |= NCP_OPT_PERMANENT;
375		break;
376	    case 'R':
377		li->retry_count = atoi(arg);
378		break;
379	    case 'W':
380		li->timeout = atoi(arg);
381		break;
382	}
383	return error;
384}
385
386void *
387ncp_conn_list(void) {
388	int error, cnt = 0, len;
389	void *p;
390
391	cnt = ncp_conn_cnt();
392	if (cnt == 0) return NULL;
393	len = cnt*(sizeof(struct ncp_conn_stat))+sizeof(int);
394	p = malloc(len);
395	if (p == NULL) return NULL;
396#if __FreeBSD_version < 400001
397	error = sysctlbyname("net.ipx.ncp.conn_stat", p, &len, NULL, 0);
398#else
399	error = sysctlbyname("net.ncp.conn_stat", p, &len, NULL, 0);
400#endif
401	if (error) {
402		free(p);
403		p = NULL;
404	}
405	return p;
406}
407
408
409int
410ncp_conn_setflags(int connid, u_int16_t mask, u_int16_t flags) {
411	int error;
412	DECLARE_RQ;
413
414	ncp_init_request(conn);
415	ncp_add_byte(conn, NCP_CONN_SETFLAGS);
416	ncp_add_word_lh(conn, mask);
417	ncp_add_word_lh(conn, flags);
418	if ((error = ncp_conn_request(connid, conn)) < 0)
419		return -1;
420	return error;
421}
422
423int
424ncp_login(int connHandle, const char *user, int objtype, const char *password) {
425	int error;
426	struct ncp_conn_login *p;
427	DECLARE_RQ;
428
429	ncp_init_request(conn);
430	ncp_add_byte(conn, NCP_CONN_LOGIN);
431	p = (struct ncp_conn_login *)&conn->packet[conn->rqsize];
432	(const char*)p->username = user;
433	p->objtype = objtype;
434	(const char*)p->password = password;
435	conn->rqsize += sizeof(*p);
436	if ((error = ncp_conn_request(connHandle, conn)) < 0)
437		return -1;
438	return error;
439}
440
441int
442ncp_connect_addr(struct sockaddr *sa, NWCONN_HANDLE *chp) {
443	int error;
444	struct ncp_conn_args li;
445
446	bzero(&li, sizeof(li));
447	bcopy(sa, &li.addr, sa->sa_len);
448	/*
449	 * XXX Temporary !!!. server will be filled in kernel !!!
450	 */
451	strcpy(li.server,ipx_ntoa(li.ipxaddr.sipx_addr));
452	error = ncp_connect(&li, chp);
453	return error;
454}
455
456int
457ncp_conn_getinfo(int connHandle, struct ncp_conn_stat *ps) {
458	int error;
459	DECLARE_RQ;
460
461	ncp_init_request(conn);
462	ncp_add_byte(conn, NCP_CONN_GETINFO);
463	if ((error = ncp_conn_request(connHandle, conn)) < 0)
464		return -1;
465	memcpy(ps, ncp_reply_data(conn,0), sizeof(*ps));
466	return error;
467}
468
469int
470ncp_conn_getuser(int connHandle, char **user) {
471	int error;
472	DECLARE_RQ;
473
474	ncp_init_request(conn);
475	ncp_add_byte(conn, NCP_CONN_GETUSER);
476	if ((error = ncp_conn_request(connHandle, conn)) < 0)
477		return -1;
478	*user = strdup(ncp_reply_data(conn,0));
479	return error;
480}
481
482int
483ncp_conn2ref(int connHandle, int *connRef) {
484	int error;
485	DECLARE_RQ;
486
487	ncp_init_request(conn);
488	ncp_add_byte(conn, NCP_CONN_CONN2REF);
489	if ((error = ncp_conn_request(connHandle, conn)) < 0)
490		return -1;
491	*connRef = *((int*)ncp_reply_data(conn,0));
492	return error;
493}
494
495int
496ncp_path2conn(char *path, int *connHandle) {
497	struct statfs st;
498	int d, error;
499
500	if ((error = statfs(path, &st)) != 0) return errno;
501	if (strcmp(st.f_fstypename,"nwfs") != 0) return EINVAL;
502	if ((d = open(path, O_RDONLY)) < 0) return errno;
503	if ((error = ioctl(d,NWFSIOC_GETCONN, connHandle)) != 0) return errno;
504	close(d);
505	return 0;
506}
507
508int
509ncp_conn_dup(NWCONN_HANDLE org, NWCONN_HANDLE *res) {
510	int error;
511	DECLARE_RQ;
512
513	ncp_init_request(conn);
514	ncp_add_byte(conn, NCP_CONN_DUP);
515	if ((error = ncp_conn_request(org, conn)) < 0)
516		return errno;
517	*res = *((int*)ncp_reply_data(conn, 0));
518	return 0;
519}
520