Example #1
0
/* the volatile qualifiers should be removed as well.                  */ 
int callmgr_main(int argc, char **argv, char **envp) {
  struct in_addr inetaddr;
  int inet_sock, unix_sock;
  fd_set call_set;
  PPTP_CONN * conn;
  VECTOR * call_list;
  int max_fd=0;
  volatile int first=1;
  int retval;
  int i;
  char * volatile phonenr;

  /* Step 0: Check arguments */
  if (argc < 2) 
    fatal("Usage: %s ip.add.ress.here [--phone <phone number>]", argv[0]);
  phonenr = argc==3 ? argv[2] : NULL;
  if (inet_aton(argv[1], &inetaddr)==0)
    fatal("Invalid IP address: %s", argv[1]);

  /* Step 1: Open sockets. */
  if ((inet_sock = open_inetsock(inetaddr)) < 0)
    fatal("Could not open control connection to %s", argv[1]);
  if ((unix_sock = open_unixsock(inetaddr)) < 0)
    fatal("Could not open unix socket for %s", argv[1]);

  /* Step 1b: FORK and return status to calling process. */
  switch (fork()) {
  case 0: /* child. stick around. */
    break;
  case -1: /* failure.  Fatal. */
    fatal("Could not fork.");
  default: /* Parent. Return status to caller. */
    exit(0);
  }

  /* re-open stderr as /dev/null to release it */
  file2fd("/dev/null", "wb", STDERR_FILENO);

  /* Step 1c: Clean up unix socket on TERM */
  if (sigsetjmp(callmgr_env, 1)!=0)
    goto cleanup;

  signal(SIGINT, callmgr_sighandler);
  signal(SIGTERM, callmgr_sighandler);

  signal(SIGPIPE, callmgr_do_nothing);
  signal(SIGUSR1, callmgr_do_nothing); /* signal state change; wake up accept */

  /* Step 2: Open control connection and register callback */
  if ((conn = pptp_conn_open(inet_sock, 1, NULL/* callback */)) == NULL) {
    close(unix_sock); close(inet_sock); fatal("Could not open connection.");
  }

  FD_ZERO(&call_set);
  max_fd = unix_sock;
  call_list = vector_create();
  { 
    struct local_conninfo *conninfo = malloc(sizeof(*conninfo));
    if (conninfo==NULL) {
      close(unix_sock); close(inet_sock); fatal("No memory.");
    }
    conninfo->call_list = call_list;
    conninfo->call_set  = &call_set;
    pptp_conn_closure_put(conn, conninfo);
  }

  if (sigsetjmp(callmgr_env, 1)!=0) goto shutdown;

  /* Step 3: Get FD_SETs */
  do {
    int rc;
    fd_set read_set = call_set, write_set;
    FD_ZERO (&write_set);
    FD_SET (unix_sock, &read_set);
    pptp_fd_set(conn, &read_set, &write_set, &max_fd);

    for (; max_fd > 0 ; max_fd--) {
      if (FD_ISSET (max_fd, &read_set) ||
          FD_ISSET (max_fd, &write_set))
        break;
    }

    /* Step 4: Wait on INET or UNIX event */

    if ((rc = select(max_fd+1, &read_set, &write_set, NULL, NULL)) <0)
      /* a signal or somesuch. */
      continue;

    /* Step 5a: Handle INET events */
    pptp_dispatch(conn, &read_set, &write_set);

    /* Step 5b: Handle new connection to UNIX socket */
    if (FD_ISSET(unix_sock, &read_set)) {
      /* New call! */
      struct sockaddr_un from;
      int len = sizeof(from);
      PPTP_CALL * call;
      struct local_callinfo *lci;
      int s;

      /* Accept the socket */
      FD_CLR (unix_sock, &read_set);
      if ((s = accept(unix_sock, (struct sockaddr *) &from, &len))<0) {
	warn("Socket not accepted: %s", strerror(errno));
	goto skip_accept;
      }
      /* Allocate memory for local call information structure. */
      if ((lci = malloc(sizeof(*lci))) == NULL) {
	warn("Out of memory."); close(s); goto skip_accept;
      }
      lci->unix_sock = s;

      /* Give the initiator time to write the PIDs while we open the call */
      call = pptp_call_open(conn, call_callback, phonenr);
      /* Read and store the associated pids */
      read(s, &lci->pid[0], sizeof(lci->pid[0]));
      read(s, &lci->pid[1], sizeof(lci->pid[1]));
      /* associate the local information with the call */
      pptp_call_closure_put(conn, call, (void *) lci);
      /* The rest is done on callback. */
      
      /* Keep alive; wait for close */
      retval = vector_insert(call_list, s, call); assert(retval);
      if (s > max_fd) max_fd = s;
      FD_SET(s, &call_set);
      first = 0;
    }
  skip_accept:
    /* Step 5c: Handle socket close */
    for (i=0; i<max_fd+1; i++)
      if (FD_ISSET(i, &read_set)) {
	/* close it */
	PPTP_CALL * call;
	retval = vector_search(call_list, i, &call);
	if (retval) {
	  struct local_callinfo *lci = pptp_call_closure_get(conn, call);
          log("Closing connection");
	  if(lci->pid[0]) kill(lci->pid[0], SIGTERM);
	  if(lci->pid[1]) kill(lci->pid[1], SIGTERM);
	  free(lci);
	  /* soft shutdown.  Callback will do hard shutdown later */
	  pptp_call_close(conn, call);
	  vector_remove(call_list, i);
	}
	FD_CLR(i, &call_set);
	close(i);
      }
  } while (vector_size(call_list)>0 || first);

shutdown:
  {
    fd_set read_set, write_set;

    /* warn("Shutdown"); */
    /* kill all open calls */
    for (i=0; i<vector_size(call_list); i++) {
      PPTP_CALL *call = vector_get_Nth(call_list, i);
      struct local_callinfo *lci = pptp_call_closure_get(conn, call);
      log("Closing connection");
      pptp_call_close(conn, call);
      if(lci->pid[0]) kill(lci->pid[0], SIGTERM);
      if(lci->pid[1]) kill(lci->pid[1], SIGTERM);
    }
    /* attempt to dispatch these messages */
    FD_ZERO(&read_set);
    FD_ZERO(&write_set);
    pptp_fd_set(conn, &read_set, &write_set, &max_fd);
    FD_ZERO(&read_set);
    pptp_dispatch(conn, &read_set, &write_set);
    if (i>0) sleep(2);
    /* no more open calls.  Close the connection. */
    pptp_conn_close(conn, PPTP_STOP_LOCAL_SHUTDOWN);
    FD_ZERO(&read_set);
    FD_ZERO(&write_set);
    pptp_fd_set(conn, &read_set, &write_set, &max_fd);
    FD_ZERO(&read_set);
    pptp_dispatch(conn, &read_set, &write_set);
    sleep(2);
    /* with extreme prejudice */
    pptp_conn_destroy(conn);
    vector_destroy(call_list);
  }
cleanup:
  close_inetsock(inet_sock, inetaddr);
  close_unixsock(unix_sock, inetaddr);
  return 0;
}
Example #2
0
/* TODO: redesign to avoid longjmp/setjmp.  Several variables here
   have a volatile qualifier to silence warnings from gcc < 3.0.
   Remove the volatile qualifiers if longjmp/setjmp are removed.
   */
int main(int argc, char **argv, char **envp)
{
    struct in_addr inetaddr;
    volatile int callmgr_sock = -1;
    char ttydev[PATH_MAX];
    int pty_fd, tty_fd, gre_fd, rc;
    volatile pid_t parent_pid, child_pid;
    u_int16_t call_id, peer_call_id;
    char buf[128];
    int pppdargc;
    char **pppdargv;
    char phonenrbuf[65]; /* maximum length of field plus one for the trailing
                          * '\0' */
    char * volatile phonenr = NULL;
    volatile int launchpppd = 1, debug = 0, nodaemon = 0;

    while(1){ 
        /* structure with all recognised options for pptp */
        static struct option long_options[] = {
            {"phone", 1, 0, 0},  
            {"nolaunchpppd", 0, 0, 0},  
            {"quirks", 1, 0, 0},
            {"debug", 0, 0, 0},
            {"sync", 0, 0, 0},
            {"timeout", 1, 0, 0},
            {"logstring", 1, 0, 0},
            {"localbind", 1, 0, 0},
            {"loglevel", 1, 0, 0},
	    {"nobuffer", 0, 0, 0},
	    {"idle-wait", 1, 0, 0},
	    {"max-echo-wait", 1, 0, 0},
	    {"version", 0, 0, 0},
            {"nodaemon", 0, 0, 0},
            {0, 0, 0, 0}
        };
        int option_index = 0;
        int c;
        c = getopt_long (argc, argv, "", long_options, &option_index);
        if (c == -1) break;  /* no more options */
        switch (c) {
            case 0: 
                if (option_index == 0) { /* --phone specified */
                    /* copy it to a buffer, as the argv's will be overwritten
                     * by inststr() */
                    strncpy(phonenrbuf,optarg,sizeof(phonenrbuf));
                    phonenrbuf[sizeof(phonenrbuf) - 1] = '\0';
                    phonenr = phonenrbuf;
                } else if (option_index == 1) {/* --nolaunchpppd specified */
                    launchpppd = 0;
                } else if (option_index == 2) {/* --quirks specified */
                    if (set_quirk_index(find_quirk(optarg)))
                        usage(argv[0]);
                } else if (option_index == 3) {/* --debug */
                    debug = 1;
                } else if (option_index == 4) {/* --sync specified */
                    syncppp = 1;
                } else if (option_index == 5) {/* --timeout */
                    float new_packet_timeout = atof(optarg);
                    if (new_packet_timeout < 0.0099 ||
                            new_packet_timeout > 10) {
                        fprintf(stderr, "Packet timeout %s (%f) out of range: "
                                "should be between 0.01 and 10 seconds\n",
                                optarg, new_packet_timeout);
                        log("Packet timeout %s (%f) out of range: should be"
                                "between 0.01 and 10 seconds", optarg,
                                new_packet_timeout);
                        exit(2);
                    } else {
                        packet_timeout_usecs = new_packet_timeout * 1000000;
                    }
                } else if (option_index == 6) {/* --logstring */
                    log_string = strdup(optarg);
                } else if (option_index == 7) {/* --localbind */ 
                    if (inet_pton(AF_INET, optarg, (void *) &localbind) < 1) {
                        fprintf(stderr, "Local bind address %s invalid\n", 
				optarg);
                        log("Local bind address %s invalid\n", optarg);
                        exit(2);
                    }
                } else if (option_index == 8) { /* --loglevel */
                    log_level = atoi(optarg);
                    if (log_level < 0 || log_level > 2)
                        usage(argv[0]);
                } else if (option_index == 9) { /* --nobuffer */
		    disable_buffer = 1;
                } else if (option_index == 10) { /* --idle-wait */
                    int x = atoi(optarg);
                    if (x < 0) {
                        fprintf(stderr, "--idle-wait must not be negative\n");
                        log("--idle-wait must not be negative\n");
                        exit(2);
                    } else {
                        idle_wait = x;
                    }
                } else if (option_index == 11) { /* --max-echo-wait */
                    int x = atoi(optarg);
                    if (x < 0) {
                        fprintf(stderr, "--max-echo-wait must not be negative\n");
                        log("--max-echo-wait must not be negative\n");
                        exit(2);
                    } else {
                        max_echo_wait = x;
                    }
		    fprintf(stderr, "--max-echo-wait ignored, not yet implemented\n");
                } else if (option_index == 12) { /* --version */
		    fprintf(stdout, "%s\n", version);
		    exit(0);
                } else if (option_index == 13) {/* --nodaemon */
                    nodaemon = 1;
                }
                break;
            case '?': /* unrecognised option */
                /* fall through */
            default:
		usage(argv[0]);
        }
        if (c == -1) break;  /* no more options for pptp */
    }

    /* at least one argument is required */
    if (argc <= optind)
        usage(argv[0]);

    /* Get IP address for the hostname in argv[1] */
    inetaddr = get_ip_address(argv[optind]);
    optind++;

    /* Find the ppp options, extract phone number */
    pppdargc = argc - optind;
    pppdargv = argv + optind;
    log("The synchronous pptp option is %sactivated\n", syncppp ? "" : "NOT ");

    /* Now we have the peer address, bind the GRE socket early,
       before starting pppd. This prevents the ICMP Unreachable bug
       documented in <1026868263.2855.67.camel@jander> */
    gre_fd = pptp_gre_bind(inetaddr);
    if (gre_fd < 0) {
        close(callmgr_sock);
        fatal("Cannot bind GRE socket, aborting.");
    }

    /* Find an open pty/tty pair. */
    if(launchpppd){
        rc = openpty (&pty_fd, &tty_fd, ttydev, NULL, NULL);
        if (rc < 0) { 
            close(callmgr_sock); 
            fatal("Could not find free pty.");
        }

#if defined(__linux__)
		/*
		 * if we do not turn off echo now,  then if pppd sleeps for any
		 * length of time (DNS timeouts or whatever) the other end of the
		 * connect may detect the link as looped.  Other OSen may want this
		 * as well,  but for now linux gets it.
		 */
		{
			struct termios tios;
			tcgetattr(tty_fd, &tios);
			cfmakeraw(&tios);
			tcsetattr(tty_fd, TCSAFLUSH, &tios);
		}
#endif

        /* fork and wait. */
        signal(SIGUSR1, do_nothing); /* don't die */
        signal(SIGCHLD, do_nothing); /* don't ignore SIGCHLD */
        parent_pid = getpid();
        switch (child_pid = fork()) {
            case -1:
                fatal("Could not fork pppd process");
            case 0: /* I'm the child! */
                close (tty_fd);
                signal(SIGUSR1, SIG_DFL);
                child_pid = getpid();
                break;
            default: /* parent */
                close (pty_fd);
                close (gre_fd);
                /*
                 * There is still a very small race condition here.  If a signal
                 * occurs after signaled is checked but before pause is called,
                 * things will hang.
                 */
                if (!signaled) {
                    pause(); /* wait for the signal */
                }
 
                if (signaled == SIGCHLD)
                    fatal("Child process died");
 
                launch_pppd(ttydev, pppdargc, pppdargv); /* launch pppd */
                perror("Error");
                fatal("Could not launch pppd");
        }
    } else { /* ! launchpppd */
        pty_fd = tty_fd = STDIN_FILENO;
        /* close unused file descriptor, that is redirected to the pty */
        close(STDOUT_FILENO);
        child_pid = getpid();
        parent_pid = 0; /* don't kill pppd */
    }

    do {
        /*
         * Open connection to call manager (Launch call manager if necessary.)
         */
        callmgr_sock = open_callmgr(inetaddr, phonenr, argc, argv, envp,
		pty_fd, gre_fd);
        /* Exchange PIDs, get call ID */
    } while (get_call_id(callmgr_sock, parent_pid, child_pid, 
                &call_id, &peer_call_id) < 0);

    /* Send signal to wake up pppd task */
    if (launchpppd) {
        kill(parent_pid, SIGUSR1);
        sleep(2);
        /* become a daemon */
        if (!(debug || nodaemon) && daemon(0, 0) != 0) {
            perror("daemon");
        }
    } else {
        /* re-open stderr as /dev/null to release it */
        file2fd("/dev/null", "wb", STDERR_FILENO);
    }

    snprintf(buf, sizeof(buf), "pptp: GRE-to-PPP gateway on %s", 
            ttyname(tty_fd));
#ifdef PR_SET_NAME
    rc = prctl(PR_SET_NAME, "pptpgw", 0, 0, 0);
    if (rc != 0) perror("prctl");
#endif
    inststr(argc, argv, envp, buf);
    if (sigsetjmp(env, 1)!= 0) goto shutdown;

    signal(SIGINT,  sighandler);
    signal(SIGTERM, sighandler);
    signal(SIGKILL, sighandler);
    signal(SIGCHLD, sighandler);
    signal(SIGUSR1, sigstats);

    /* Do GRE copy until close. */
    pptp_gre_copy(call_id, peer_call_id, pty_fd, gre_fd);

shutdown:
    /* on close, kill all. */
    if(launchpppd)
        kill(parent_pid, SIGTERM);
    close(pty_fd);
    close(callmgr_sock);
    exit(0);
}