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