ncpl_conn.c revision 84768
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 33#include <sys/cdefs.h> 34__FBSDID("$FreeBSD: head/lib/libncp/ncpl_conn.c 84768 2001-10-10 17:48:44Z bde $"); 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 <fs/nwfs/nwfs.h> 67 68static char *server_name; /* need a better way ! */ 69 70 71 72int 73ncp_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 82int 83ncp_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 100int 101ncp_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 */ 122int 123ncp_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 153void 154ncp_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 */ 165int 166ncp_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 */ 190int 191ncp_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 */ 244int 245ncp_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 275int 276ncp_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 */ 291int 292ncp_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 314int 315ncp_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 387void * 388ncp_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 410int 411ncp_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 424int 425ncp_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 442int 443ncp_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 457int 458ncp_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 470int 471ncp_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 483int 484ncp_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 496int 497ncp_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 509int 510ncp_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} 521