1 /* 2 * User Process PPP 3 * 4 * Written by Toshiharu OHNO (tony-o@iij.ad.jp) 5 * 6 * Copyright (C) 1993, Internet Initiative Japan, Inc. All rights reserverd. 7 * 8 * Redistribution and use in source and binary forms are permitted 9 * provided that the above copyright notice and this paragraph are 10 * duplicated in all such forms and that any documentation, 11 * advertising materials, and other materials related to such 12 * distribution and use acknowledge that the software was developed 13 * by the Internet Initiative Japan, Inc. The name of the 14 * IIJ may not be used to endorse or promote products derived 15 * from this software without specific prior written permission. 16 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 17 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 18 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 19 * 20 * $Id: main.c,v 1.142 1998/08/09 09:13:54 brian Exp $ 21 * 22 * TODO: 23 */ 24 25 #include <sys/types.h> 26 #include <netinet/in.h> 27 #include <netinet/in_systm.h> 28 #include <netinet/ip.h> 29 #include <sys/un.h> 30 31 #include <errno.h> 32 #include <fcntl.h> 33 #include <paths.h> 34 #include <signal.h> 35 #include <stdio.h> 36 #include <string.h> 37 #include <sys/time.h> 38 #include <termios.h> 39 #include <unistd.h> 40 41 #ifndef NOALIAS 42 #ifdef __OpenBSD__ 43 #include "alias.h" 44 #else 45 #include <alias.h> 46 #endif 47 #endif 48 #include "probe.h" 49 #include "mbuf.h" 50 #include "log.h" 51 #include "defs.h" 52 #include "id.h" 53 #include "timer.h" 54 #include "fsm.h" 55 #include "lqr.h" 56 #include "hdlc.h" 57 #include "lcp.h" 58 #include "ccp.h" 59 #include "iplist.h" 60 #include "throughput.h" 61 #include "slcompress.h" 62 #include "ipcp.h" 63 #include "filter.h" 64 #include "descriptor.h" 65 #include "link.h" 66 #include "mp.h" 67 #include "bundle.h" 68 #include "auth.h" 69 #include "systems.h" 70 #include "sig.h" 71 #include "main.h" 72 #include "server.h" 73 #include "prompt.h" 74 #include "chat.h" 75 #include "chap.h" 76 #include "cbcp.h" 77 #include "datalink.h" 78 79 #ifndef O_NONBLOCK 80 #ifdef O_NDELAY 81 #define O_NONBLOCK O_NDELAY 82 #endif 83 #endif 84 85 static void DoLoop(struct bundle *); 86 static void TerminalStop(int); 87 static const char *ex_desc(int); 88 89 static struct bundle *SignalBundle; 90 static struct prompt *SignalPrompt; 91 92 void 93 Cleanup(int excode) 94 { 95 SignalBundle->CleaningUp = 1; 96 bundle_Close(SignalBundle, NULL, CLOSE_STAYDOWN); 97 } 98 99 void 100 AbortProgram(int excode) 101 { 102 server_Close(SignalBundle); 103 log_Printf(LogPHASE, "PPP Terminated (%s).\n", ex_desc(excode)); 104 bundle_Close(SignalBundle, NULL, CLOSE_STAYDOWN); 105 bundle_Destroy(SignalBundle); 106 log_Close(); 107 exit(excode); 108 } 109 110 static void 111 CloseConnection(int signo) 112 { 113 /* NOTE, these are manual, we've done a setsid() */ 114 sig_signal(SIGINT, SIG_IGN); 115 log_Printf(LogPHASE, "Caught signal %d, abort connection(s)\n", signo); 116 bundle_Down(SignalBundle, CLOSE_STAYDOWN); 117 sig_signal(SIGINT, CloseConnection); 118 } 119 120 static void 121 CloseSession(int signo) 122 { 123 log_Printf(LogPHASE, "Signal %d, terminate.\n", signo); 124 Cleanup(EX_TERM); 125 } 126 127 static pid_t BGPid = 0; 128 129 static void 130 KillChild(int signo) 131 { 132 log_Printf(LogPHASE, "Parent: Signal %d\n", signo); 133 kill(BGPid, SIGINT); 134 } 135 136 static void 137 TerminalCont(int signo) 138 { 139 signal(SIGCONT, SIG_DFL); 140 prompt_Continue(SignalPrompt); 141 } 142 143 static void 144 TerminalStop(int signo) 145 { 146 prompt_Suspend(SignalPrompt); 147 signal(SIGCONT, TerminalCont); 148 raise(SIGSTOP); 149 } 150 151 static void 152 BringDownServer(int signo) 153 { 154 /* Drops all child prompts too ! */ 155 server_Close(SignalBundle); 156 } 157 158 static const char * 159 ex_desc(int ex) 160 { 161 static char num[12]; /* Used immediately if returned */ 162 static const char *desc[] = { 163 "normal", "start", "sock", "modem", "dial", "dead", "done", 164 "reboot", "errdead", "hangup", "term", "nodial", "nologin" 165 }; 166 167 if (ex >= 0 && ex < sizeof desc / sizeof *desc) 168 return desc[ex]; 169 snprintf(num, sizeof num, "%d", ex); 170 return num; 171 } 172 173 static void 174 Usage(void) 175 { 176 fprintf(stderr, 177 "Usage: ppp [-auto | -background | -direct | -dedicated | -ddial ]" 178 #ifndef NOALIAS 179 " [ -alias ]" 180 #endif 181 " [system]\n"); 182 exit(EX_START); 183 } 184 185 static char * 186 ProcessArgs(int argc, char **argv, int *mode, int *alias) 187 { 188 int optc, labelrequired, newmode; 189 char *cp; 190 191 optc = labelrequired = 0; 192 *mode = PHYS_INTERACTIVE; 193 *alias = 0; 194 while (argc > 0 && **argv == '-') { 195 cp = *argv + 1; 196 newmode = Nam2mode(cp); 197 switch (newmode) { 198 case PHYS_NONE: 199 if (strcmp(cp, "alias") == 0) { 200 #ifdef NOALIAS 201 log_Printf(LogWARN, "Cannot load alias library\n"); 202 #else 203 *alias = 1; 204 #endif 205 optc--; /* this option isn't exclusive */ 206 } else 207 Usage(); 208 break; 209 210 case PHYS_ALL: 211 Usage(); 212 break; 213 214 case PHYS_AUTO: 215 case PHYS_BACKGROUND: 216 case PHYS_DDIAL: 217 labelrequired = 1; 218 /* fall through */ 219 220 default: 221 *mode = newmode; 222 } 223 optc++; 224 argv++; 225 argc--; 226 } 227 228 if (argc > 1) { 229 fprintf(stderr, "You may specify only one system label.\n"); 230 exit(EX_START); 231 } 232 233 if (optc > 1) { 234 fprintf(stderr, "You may specify only one mode.\n"); 235 exit(EX_START); 236 } 237 238 if (labelrequired && argc != 1) { 239 fprintf(stderr, "Destination system must be specified in" 240 " auto, background or ddial mode.\n"); 241 exit(EX_START); 242 } 243 244 return argc == 1 ? *argv : NULL; /* Don't SetLabel yet ! */ 245 } 246 247 int 248 main(int argc, char **argv) 249 { 250 char *name, *label; 251 int nfds, mode, alias; 252 struct bundle *bundle; 253 struct prompt *prompt; 254 255 nfds = getdtablesize(); 256 if (nfds >= FD_SETSIZE) 257 /* 258 * If we've got loads of file descriptors, make sure they're all 259 * closed. If they aren't, we may end up with a seg fault when our 260 * `fd_set's get too big when select()ing ! 261 */ 262 while (--nfds > 2) 263 close(nfds); 264 265 name = strrchr(argv[0], '/'); 266 log_Open(name ? name + 1 : argv[0]); 267 268 #ifndef NOALIAS 269 PacketAliasInit(); 270 #endif 271 label = ProcessArgs(argc - 1, argv + 1, &mode, &alias); 272 273 #ifdef __FreeBSD__ 274 /* 275 * A FreeBSD hack to dodge a bug in the tty driver that drops output 276 * occasionally.... I must find the real reason some time. To display 277 * the dodgy behaviour, comment out this bit, make yourself a large 278 * routing table and then run ppp in interactive mode. The `show route' 279 * command will drop chunks of data !!! 280 */ 281 if (mode == PHYS_INTERACTIVE) { 282 close(STDIN_FILENO); 283 if (open(_PATH_TTY, O_RDONLY) != STDIN_FILENO) { 284 fprintf(stderr, "Cannot open %s for input !\n", _PATH_TTY); 285 return 2; 286 } 287 } 288 #endif 289 290 /* Allow output for the moment (except in direct mode) */ 291 if (mode == PHYS_DIRECT) 292 prompt = NULL; 293 else { 294 SignalPrompt = prompt = prompt_Create(NULL, NULL, PROMPT_STD); 295 prompt_Printf(prompt, "Working in %s mode\n", mode2Nam(mode)); 296 } 297 298 ID0init(); 299 if (ID0realuid() != 0) { 300 char conf[200], *ptr; 301 302 snprintf(conf, sizeof conf, "%s/%s", _PATH_PPP, CONFFILE); 303 do { 304 if (!access(conf, W_OK)) { 305 log_Printf(LogALERT, "ppp: Access violation: Please protect %s\n", 306 conf); 307 return -1; 308 } 309 ptr = conf + strlen(conf)-2; 310 while (ptr > conf && *ptr != '/') 311 *ptr-- = '\0'; 312 } while (ptr >= conf); 313 } 314 315 if (!system_IsValid(label, prompt, mode)) { 316 fprintf(stderr, "You may not use ppp in this mode with this label\n"); 317 if (mode == PHYS_DIRECT) { 318 const char *l; 319 l = label ? label : "default"; 320 log_Printf(LogWARN, "Label %s rejected -direct connection\n", l); 321 } 322 log_Close(); 323 return 1; 324 } 325 326 if ((bundle = bundle_Create(TUN_PREFIX, mode, (const char **)argv)) == NULL) { 327 log_Printf(LogWARN, "bundle_Create: %s\n", strerror(errno)); 328 return EX_START; 329 } 330 if (prompt) { 331 prompt->bundle = bundle; /* couldn't do it earlier */ 332 prompt_Printf(prompt, "Using interface: %s\n", bundle->ifp.Name); 333 } 334 SignalBundle = bundle; 335 bundle->AliasEnabled = alias; 336 337 if (system_Select(bundle, "default", CONFFILE, prompt, NULL) < 0) 338 prompt_Printf(prompt, "Warning: No default entry found in config file.\n"); 339 340 sig_signal(SIGHUP, CloseSession); 341 sig_signal(SIGTERM, CloseSession); 342 sig_signal(SIGINT, CloseConnection); 343 sig_signal(SIGQUIT, CloseSession); 344 sig_signal(SIGALRM, SIG_IGN); 345 signal(SIGPIPE, SIG_IGN); 346 347 if (mode == PHYS_INTERACTIVE) 348 sig_signal(SIGTSTP, TerminalStop); 349 350 sig_signal(SIGUSR2, BringDownServer); 351 352 if (label) { 353 /* 354 * Set label both before and after system_Select ! 355 * This way, "set enddisc label" works during system_Select, and we 356 * also end up with the correct label if we have embedded load 357 * commands. 358 */ 359 bundle_SetLabel(bundle, label); 360 if (system_Select(bundle, label, CONFFILE, prompt, NULL) < 0) { 361 prompt_Printf(prompt, "Destination system (%s) not found.\n", label); 362 AbortProgram(EX_START); 363 } 364 bundle_SetLabel(bundle, label); 365 if (mode == PHYS_AUTO && 366 bundle->ncp.ipcp.cfg.peer_range.ipaddr.s_addr == INADDR_ANY) { 367 prompt_Printf(prompt, "You must \"set ifaddr\" with a peer address " 368 "in label %s for auto mode.\n", label); 369 AbortProgram(EX_START); 370 } 371 } 372 373 if (mode != PHYS_INTERACTIVE) { 374 if (mode != PHYS_DIRECT) { 375 int bgpipe[2]; 376 pid_t bgpid; 377 378 if (mode == PHYS_BACKGROUND && pipe(bgpipe)) { 379 log_Printf(LogERROR, "pipe: %s\n", strerror(errno)); 380 AbortProgram(EX_SOCK); 381 } 382 383 bgpid = fork(); 384 if (bgpid == -1) { 385 log_Printf(LogERROR, "fork: %s\n", strerror(errno)); 386 AbortProgram(EX_SOCK); 387 } 388 389 if (bgpid) { 390 char c = EX_NORMAL; 391 392 if (mode == PHYS_BACKGROUND) { 393 close(bgpipe[1]); 394 BGPid = bgpid; 395 /* If we get a signal, kill the child */ 396 signal(SIGHUP, KillChild); 397 signal(SIGTERM, KillChild); 398 signal(SIGINT, KillChild); 399 signal(SIGQUIT, KillChild); 400 401 /* Wait for our child to close its pipe before we exit */ 402 if (read(bgpipe[0], &c, 1) != 1) { 403 prompt_Printf(prompt, "Child exit, no status.\n"); 404 log_Printf(LogPHASE, "Parent: Child exit, no status.\n"); 405 } else if (c == EX_NORMAL) { 406 prompt_Printf(prompt, "PPP enabled.\n"); 407 log_Printf(LogPHASE, "Parent: PPP enabled.\n"); 408 } else { 409 prompt_Printf(prompt, "Child failed (%s).\n", ex_desc((int) c)); 410 log_Printf(LogPHASE, "Parent: Child failed (%s).\n", 411 ex_desc((int) c)); 412 } 413 close(bgpipe[0]); 414 } 415 return c; 416 } else if (mode == PHYS_BACKGROUND) { 417 close(bgpipe[0]); 418 bundle->notify.fd = bgpipe[1]; 419 } 420 421 bundle_LockTun(bundle); /* we have a new pid */ 422 423 /* -auto, -dedicated, -ddial & -background */ 424 prompt_Destroy(prompt, 0); 425 close(STDOUT_FILENO); 426 close(STDERR_FILENO); 427 close(STDIN_FILENO); 428 setsid(); 429 } else { 430 /* -direct: STDIN_FILENO gets used by modem_Open */ 431 prompt_TtyInit(NULL); 432 close(STDOUT_FILENO); 433 close(STDERR_FILENO); 434 } 435 } else { 436 /* Interactive mode */ 437 close(STDERR_FILENO); 438 prompt_TtyInit(prompt); 439 prompt_TtyCommandMode(prompt); 440 prompt_Required(prompt); 441 } 442 443 log_Printf(LogPHASE, "PPP Started (%s mode).\n", mode2Nam(mode)); 444 DoLoop(bundle); 445 AbortProgram(EX_NORMAL); 446 447 return EX_NORMAL; 448 } 449 450 static void 451 DoLoop(struct bundle *bundle) 452 { 453 fd_set rfds, wfds, efds; 454 int i, nfds, nothing_done; 455 struct probe probe; 456 457 probe_Init(&probe); 458 459 do { 460 nfds = 0; 461 FD_ZERO(&rfds); 462 FD_ZERO(&wfds); 463 FD_ZERO(&efds); 464 465 /* All our datalinks, the tun device and the MP socket */ 466 descriptor_UpdateSet(&bundle->desc, &rfds, &wfds, &efds, &nfds); 467 468 /* All our prompts and the diagnostic socket */ 469 descriptor_UpdateSet(&server.desc, &rfds, NULL, NULL, &nfds); 470 471 if (bundle_IsDead(bundle)) 472 /* Don't select - we'll be here forever */ 473 break; 474 475 i = select(nfds, &rfds, &wfds, &efds, NULL); 476 477 if (i < 0 && errno != EINTR) { 478 log_Printf(LogERROR, "DoLoop: select(): %s\n", strerror(errno)); 479 if (log_IsKept(LogTIMER)) { 480 struct timeval t; 481 482 for (i = 0; i <= nfds; i++) { 483 if (FD_ISSET(i, &rfds)) { 484 log_Printf(LogTIMER, "Read set contains %d\n", i); 485 FD_CLR(i, &rfds); 486 t.tv_sec = t.tv_usec = 0; 487 if (select(nfds, &rfds, &wfds, &efds, &t) != -1) { 488 log_Printf(LogTIMER, "The culprit !\n"); 489 break; 490 } 491 } 492 if (FD_ISSET(i, &wfds)) { 493 log_Printf(LogTIMER, "Write set contains %d\n", i); 494 FD_CLR(i, &wfds); 495 t.tv_sec = t.tv_usec = 0; 496 if (select(nfds, &rfds, &wfds, &efds, &t) != -1) { 497 log_Printf(LogTIMER, "The culprit !\n"); 498 break; 499 } 500 } 501 if (FD_ISSET(i, &efds)) { 502 log_Printf(LogTIMER, "Error set contains %d\n", i); 503 FD_CLR(i, &efds); 504 t.tv_sec = t.tv_usec = 0; 505 if (select(nfds, &rfds, &wfds, &efds, &t) != -1) { 506 log_Printf(LogTIMER, "The culprit !\n"); 507 break; 508 } 509 } 510 } 511 } 512 break; 513 } 514 515 sig_Handle(); 516 517 if (i <= 0) 518 continue; 519 520 for (i = 0; i <= nfds; i++) 521 if (FD_ISSET(i, &efds)) { 522 log_Printf(LogERROR, "Exception detected on descriptor %d\n", i); 523 break; 524 } 525 526 if (i <= nfds) 527 break; 528 529 nothing_done = 1; 530 531 if (descriptor_IsSet(&server.desc, &rfds)) { 532 descriptor_Read(&server.desc, bundle, &rfds); 533 nothing_done = 0; 534 } 535 536 if (descriptor_IsSet(&bundle->desc, &rfds)) { 537 descriptor_Read(&bundle->desc, bundle, &rfds); 538 nothing_done = 0; 539 } 540 541 if (descriptor_IsSet(&bundle->desc, &wfds)) 542 if (!descriptor_Write(&bundle->desc, bundle, &wfds) && nothing_done) { 543 /* 544 * This is disasterous. The OS has told us that something is 545 * writable, and all our write()s have failed. Rather than 546 * going back immediately to do our UpdateSet()s and select(), 547 * we sleep for a bit to avoid gobbling up all cpu time. 548 */ 549 struct timeval t; 550 551 t.tv_sec = 0; 552 t.tv_usec = 100000; 553 select(0, NULL, NULL, NULL, &t); 554 } 555 556 } while (bundle_CleanDatalinks(bundle), !bundle_IsDead(bundle)); 557 558 log_Printf(LogDEBUG, "DoLoop done.\n"); 559 } 560