Initial import from FreeBSD RELENG_4:
[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  */
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
67 static char *server_name; /* need a better way ! */
68
69
70
71 int
72 ncp_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
81 int
82 ncp_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
99 int
100 ncp_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  */
121 int
122 ncp_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
152 void
153 ncp_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  */
164 int
165 ncp_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  */
189 int
190 ncp_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  */
243 int
244 ncp_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
274 int
275 ncp_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  */
290 int
291 ncp_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
313 int
314 ncp_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
386 void *
387 ncp_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
409 int
410 ncp_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
423 int
424 ncp_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
441 int
442 ncp_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
456 int
457 ncp_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
469 int
470 ncp_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
482 int
483 ncp_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
495 int
496 ncp_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
508 int
509 ncp_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 }