1 #include <sys/types.h> 2 #include <sys/socket.h> 3 #include <netinet/in.h> 4 #include <sys/wait.h> 5 6 #include <errno.h> 7 extern int errno; 8 9 #include <netdb.h> 10 #include <signal.h> 11 #include <stdio.h> 12 #include <pwd.h> 13 14 #include "../general/general.h" 15 #include "../api/api.h" 16 #include "api_exch.h" 17 18 #include "../general/globals.h" 19 20 21 static int shell_pid = 0; 22 23 static char *ourENVlist[200]; /* Lots of room */ 24 25 static int sock = -1; 26 27 static enum { DEAD, UNCONNECTED, CONNECTED } state; 28 29 static int 30 storage_address, /* Address we have */ 31 storage_length = 0, /* Length we have */ 32 storage_must_send = 0, /* Storage belongs on other side of wire */ 33 storage_accessed = 0; /* The storage is accessed (so leave alone)! */ 34 35 static long storage[250]; 36 37 union REGS inputRegs; 38 struct SREGS inputSregs; 39 40 kill_connection() 41 { 42 state = DEAD; 43 (void) close(sock); 44 sock = -1; 45 } 46 47 48 static int 49 child_died() 50 { 51 union wait *status; 52 register int pid; 53 54 while ((pid = wait3(&status, WNOHANG, 0)) > 0) { 55 if (pid == shell_pid) { 56 char inputbuffer[100]; 57 58 shell_active = 0; 59 if (sock != -1) { 60 (void) close(sock); 61 sock = -1; 62 printf("[Hit return to continue]"); 63 fflush(stdout); 64 (void) gets(inputbuffer); 65 setconnmode(); 66 ConnectScreen(); /* Turn screen on (if need be) */ 67 } 68 } 69 } 70 signal(SIGCHLD, child_died); 71 } 72 73 static int 74 nextchar() 75 { 76 unsigned char c; 77 78 if (read(sock, &c, 1) != 1) { 79 return -1; 80 } else { 81 return c; 82 } 83 } 84 85 static int 86 checktype(type) 87 int type; 88 { 89 int was; 90 91 if ((was = nextchar()) != type) { 92 fprintf(stderr, "Wrong type of data. Should be 0x%02x, was 0x%02x.\n", 93 type, was); 94 return -1; 95 } else { 96 return 0; 97 } 98 } 99 100 101 static int 102 fill(where, length) 103 char *where; 104 int length; 105 { 106 while (length) { 107 int i; 108 109 if ((i = read(sock, where, length)) < 0) { 110 perror("read"); 111 return -1; 112 } else { 113 length -= i; 114 where += i; 115 } 116 } 117 } 118 119 120 static int 121 nextlength() 122 { 123 short length; 124 125 if (fill(&length, sizeof length) == -1) { 126 return -1; 127 } else { 128 return ntohs(length); 129 } 130 } 131 132 133 static int 134 nextaddress() 135 { 136 long address; 137 138 return fill(&address, sizeof address); 139 } 140 141 142 143 static int 144 nextbytes(type, where, length) 145 int type; 146 char *where; 147 int length; 148 { 149 int was; 150 151 if (checktype(type) == -1) { 152 return -1; 153 } 154 if ((was = nextlength()) != length) { 155 fprintf(stderr, "Type 0x%02x had bad length. Should be %d, was %d.\n", 156 type, length, was); 157 return -1; 158 } else { 159 return fill(where, length); 160 } 161 } 162 163 static int 164 nextstore() 165 { 166 if (nextchar() != EXCH_HEREIS) { 167 fprintf(stderr, "Bad data from other side.\n"); 168 fprintf(stderr, "(Encountered at %s, %s.)\n", __FILE__, __LINE__); 169 return -1; 170 } 171 if ((storage_address = nextaddress()) == -1) { 172 storage_length = 0; 173 return -1; 174 } 175 if ((storage_length = nextlength()) > sizeof storage) { 176 fprintf(stderr, "API client tried to send too much storage (%d).\n", 177 storage_length); 178 return -1; 179 } 180 return fill((char *)storage, storage_length); 181 } 182 183 184 static int 185 doreject(message) 186 char *message; 187 { 188 int length = strlen(message); 189 char buffer[100]; 190 191 length = htons(length); 192 sprintf(buffer, "%c%c%c%s", EXCH_REJECTED, length>>8, length&0xff, buffer); 193 if (write(sock, buffer, length+3) != length+3) { 194 perror("writing API socket"); 195 return -1; 196 } 197 return 0; 198 } 199 200 201 /* 202 * doconnect() 203 * 204 * Negotiate with the other side and try to do something. 205 */ 206 207 static int 208 doconnect() 209 { 210 struct passwd *pwent; 211 char 212 promptbuf[100], 213 buffer[200]; 214 int promptlen, passwdlen; 215 int length; 216 int was; 217 218 if ((pwent = getpwuid(geteuid())) == 0) { 219 return -1; 220 } 221 sprintf(promptbuf, "Enter password for user %s:", pwent->pw_name); 222 promptlen = strlen(promptbuf); 223 passwdlen = strlen(pwent->pw_name); 224 sprintf(buffer, "%c%c%c%s%c%c%s", EXCH_SEND_AUTH, 225 promptlen>>8, promptlen&0xff, promptbuf, 226 passwdlen>>8, passwdlen&0xff, pwent->pw_name); 227 length = strlen(buffer); 228 if (write(sock, buffer, length) != length) { 229 perror("write to API socket"); 230 return -1; 231 } 232 if ((was = nextchar()) != EXCH_AUTH) { 233 fprintf(stderr, 234 "API client sent command 0x%02x when EXCH_AUTH expected.\n", was); 235 } 236 if ((length = nextlength()) > sizeof buffer) { 237 doreject("Password entered was too long"); 238 return 0; 239 } 240 if (fill(buffer, length) == -1) { 241 return -1; 242 } 243 buffer[length] = 0; 244 245 /* Is this the correct password? */ 246 if (strcmp(crypt(buffer, pwent->pw_passwd), pwent->pw_passwd) == 0) { 247 char code = EXCH_CONNECTED; 248 if (write(sock, &code, 1) != 1) { 249 perror("writing to API socket"); 250 return -1; 251 } 252 } else { 253 doreject("Invalid password"); 254 sleep(10); /* Don't let us do too many of these */ 255 } 256 return 0; 257 } 258 259 260 void 261 freestorage() 262 { 263 int i, j; 264 char buffer[40]; 265 266 if (storage_accessed) { 267 fprintf(stderr, "Internal error - attempt to free accessed storage.\n"); 268 fprintf(stderr, "(Enountered in file %s at line %s.)\n", 269 __FILE__, __LINE__); 270 quit(); 271 } 272 if (storage_must_send == 0) { 273 return; 274 } 275 storage_must_send = 0; 276 i = htonl(storage_address); 277 j = htonl(storage_length); 278 sprintf(buffer, "%c%c%c%c%c%c%c", 279 EXCH_HEREIS, i>>24, i>>16, i>>8, i, j>>8, j); 280 if (write(sock, buffer, 5) != 5) { 281 perror("writing to API socket"); 282 kill_connection(); 283 return; 284 } 285 if (write(sock, (char *)storage, storage_length) != storage_length) { 286 perror("writing to API socket"); 287 kill_connection(); 288 return; 289 } 290 } 291 292 293 void 294 getstorage(address, length) 295 { 296 int i, j; 297 char buffer[40]; 298 299 freestorage(); 300 if (storage_accessed) { 301 fprintf(stderr, 302 "Internal error - attempt to get while storage accessed.\n"); 303 fprintf(stderr, "(Enountered in file %s at line %s.)\n", 304 __FILE__, __LINE__); 305 quit(); 306 } 307 if (storage_must_send == 0) { 308 return; 309 } 310 storage_must_send = 0; 311 i = htonl(storage_address); 312 j = htonl(storage_length); 313 sprintf(buffer, "%c%c%c%c%c%c%c", 314 EXCH_GIMME, i>>24, i>>16, i>>8, i, j>>8, j); 315 if (write(sock, buffer, 5) != 5) { 316 perror("writing to API socket"); 317 kill_connection(); 318 return; 319 } 320 if (nextstore() == -1) { 321 kill_connection(); 322 return; 323 } 324 } 325 326 void 327 movetous(local, es, di, length) 328 char 329 *local; 330 int 331 es, 332 di; 333 int 334 length; 335 { 336 if (length > sizeof storage) { 337 fprintf(stderr, "Internal API error - movetous() length too long.\n"); 338 fprintf(stderr, "(detected in file %s, line %d)\n", __FILE__, __LINE__); 339 quit(); 340 } else if (length == 0) { 341 return; 342 } 343 getstorage(di, length); 344 memcpy(local, storage+(di-storage_address), length); 345 } 346 347 void 348 movetothem(es, di, local, length) 349 int 350 es, 351 di; 352 char 353 *local; 354 int 355 length; 356 { 357 if (length > sizeof storage) { 358 fprintf(stderr, "Internal API error - movetothem() length too long.\n"); 359 fprintf(stderr, "(detected in file %s, line %d)\n", __FILE__, __LINE__); 360 quit(); 361 } else if (length == 0) { 362 return; 363 } 364 freestorage(); 365 memcpy((char *)storage, local, length); 366 storage_length = length; 367 storage_address = di; 368 storage_must_send = 1; 369 } 370 371 372 char * 373 access_api(location, length) 374 int 375 location, 376 length; 377 { 378 if (storage_accessed) { 379 fprintf(stderr, "Internal error - storage accessed twice\n"); 380 fprintf(stderr, "(Encountered in file %s, line %s.)\n", 381 __FILE__, __LINE__); 382 quit(); 383 } else if (length != 0) { 384 storage_accessed = 1; 385 freestorage(); 386 getstorage(location, length); 387 } 388 return (char *) storage; 389 } 390 391 unaccess_api(location, local, length) 392 int location; 393 char *local; 394 int length; 395 { 396 if (storage_accessed == 0) { 397 fprintf(stderr, "Internal error - unnecessary unaccess_api call.\n"); 398 fprintf(stderr, "(Encountered in file %s, line %s.)\n", 399 __FILE__, __LINE__); 400 quit(); 401 } 402 storage_accessed = 0; 403 storage_must_send = 1; /* Needs to go back */ 404 } 405 406 407 /* 408 * shell_continue() actually runs the command, and looks for API 409 * requests coming back in. 410 * 411 * We are called from the main loop in telnet.c. 412 */ 413 414 int 415 shell_continue() 416 { 417 switch (state) { 418 case DEAD: 419 pause(); /* Nothing to do */ 420 break; 421 case UNCONNECTED: 422 if (nextchar() != EXCH_CONNECT) { 423 kill_connection(); 424 } else { 425 switch (doconnect()) { 426 case -1: 427 kill_connection(); 428 break; 429 case 0: 430 break; 431 case 1: 432 state = CONNECTED; 433 } 434 } 435 break; 436 case CONNECTED: 437 if (nextchar() == EXCH_REQUEST) { 438 /* Eat up request packet. */ 439 nextbytes(&inputRegs, sizeof inputRegs); 440 nextbytes(&inputSregs, sizeof inputSregs); 441 nextstore(); /* Initial storage sent */ 442 handle_api(&inputRegs, &inputSregs); 443 } else { 444 kill_connection(); 445 } 446 } 447 return shell_active; 448 } 449 450 451 /* 452 * Called from telnet.c to fork a lower command.com. We 453 * use the spint... routines so that we can pick up 454 * interrupts generated by application programs. 455 */ 456 457 458 int 459 shell(argc,argv) 460 int argc; 461 char *argv[]; 462 { 463 int serversock, length; 464 struct sockaddr_in server; 465 char sockNAME[100]; 466 static char **whereAPI = 0; 467 468 /* First, create the socket which will be connected to */ 469 serversock = socket(AF_INET, SOCK_STREAM, 0); 470 if (serversock < 0) { 471 perror("opening API socket"); 472 return 0; 473 } 474 server.sin_family = AF_INET; 475 server.sin_addr.s_addr = INADDR_ANY; 476 server.sin_port = 0; 477 if (bind(serversock, &server, sizeof server) < 0) { 478 perror("binding API socket"); 479 return 0; 480 } 481 length = sizeof server; 482 if (getsockname(serversock, &server, &length) < 0) { 483 perror("getting API socket name"); 484 (void) close(serversock); 485 } 486 listen(serversock, 1); 487 /* Get name to advertise in address list */ 488 strcpy(sockNAME, "API3270="); 489 gethostname(sockNAME+strlen(sockNAME), sizeof sockNAME-strlen(sockNAME)); 490 if (strlen(sockNAME) > (sizeof sockNAME-10)) { 491 fprintf(stderr, "Local hostname too large; using 'localhost'.\n"); 492 strcpy(sockNAME, "localhost"); 493 } 494 sprintf(sockNAME+strlen(sockNAME), ":%d", ntohs(server.sin_port)); 495 496 if (whereAPI == 0) { 497 char **ptr, **nextenv; 498 extern char **environ; 499 500 ptr = environ; 501 nextenv = ourENVlist; 502 while (*ptr) { 503 if (nextenv >= &ourENVlist[highestof(ourENVlist)-1]) { 504 fprintf(stderr, "Too many environmental variables\n"); 505 break; 506 } 507 *nextenv++ = *ptr++; 508 } 509 whereAPI = nextenv++; 510 *nextenv++ = 0; 511 environ = ourENVlist; /* New environment */ 512 } 513 *whereAPI = sockNAME; 514 515 child_died(); /* Start up signal handler */ 516 shell_active = 1; /* We are running down below */ 517 if (shell_pid = vfork()) { 518 if (shell_pid == -1) { 519 perror("vfork"); 520 (void) close(serversock); 521 } else { 522 fd_set fdset; 523 int i; 524 525 FD_ZERO(&fdset); 526 FD_SET(serversock, &fdset); 527 while (shell_active) { 528 if ((i = select(serversock+1, &fdset, 0, 0, 0)) < 0) { 529 if (errno = EINTR) { 530 continue; 531 } else { 532 perror("in select waiting for API connection"); 533 break; 534 } 535 } else { 536 i = accept(serversock, 0, 0); 537 if (i == -1) { 538 perror("accepting API connection"); 539 } 540 sock = i; 541 break; 542 } 543 } 544 (void) close(serversock); 545 /* If the process has already exited, we may need to close */ 546 if ((shell_active == 0) && (sock != -1)) { 547 (void) close(sock); 548 sock = -1; 549 setcommandmode(); /* In case child_died sneaked in */ 550 } 551 } 552 } else { /* New process */ 553 register int i; 554 555 for (i = 3; i < 30; i++) { 556 (void) close(i); 557 } 558 if (argc == 1) { /* Just get a shell */ 559 char *cmdname; 560 extern char *getenv(); 561 562 cmdname = getenv("SHELL"); 563 execlp(cmdname, cmdname, 0); 564 perror("Exec'ing new shell...\n"); 565 exit(1); 566 } else { 567 execvp(argv[1], &argv[1]); 568 perror("Exec'ing command.\n"); 569 exit(1); 570 } 571 /*NOTREACHED*/ 572 } 573 return shell_active; /* Go back to main loop */ 574 } 575