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