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