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