/*** this is a soft close *****************************************************/
void pptp_conn_close(PPTP_CONN * conn, u_int8_t close_reason)
{
    struct pptp_stop_ctrl_conn rqst = {
        PPTP_HEADER_CTRL(PPTP_STOP_CTRL_CONN_RQST), 
        hton8(close_reason), 0, 0
    };
    int i;
  
    assert(conn && conn->call);

    /* avoid repeated close attempts */
    if (conn->conn_state == CONN_IDLE || conn->conn_state == CONN_WAIT_STOP_REPLY) 
        return;

    /* close open calls, if any */
    for (i = 0; i < vector_size(conn->call); i++)
        pptp_call_close(conn, vector_get_Nth(conn->call, i));
    
    /* now close connection */
    log("Closing PPTP connection");
    pptp_send_ctrl_packet(conn, &rqst, sizeof(rqst));
    pptp_reset_timer(); /* wait 60 seconds for reply */
    conn->conn_state = CONN_WAIT_STOP_REPLY;
    return;
}
/*** Handle keep-alive timer **************************************************/
static void pptp_handle_timer(int sig)
{
    int i;
    /* "Keep Alives and Timers, 1": check connection state */
    if (global.conn->conn_state != CONN_ESTABLISHED) {
        if (global.conn->conn_state == CONN_WAIT_STOP_REPLY) 
            /* hard close. */
            pptp_conn_destroy(global.conn);
        else /* soft close */
            pptp_conn_close(global.conn, PPTP_STOP_NONE);
    }
    /* "Keep Alives and Timers, 2": check echo status */
    if (global.conn->ka_state == KA_OUTSTANDING) /*no response to keep-alive*/
        pptp_conn_close(global.conn, PPTP_STOP_NONE);
    else { /* ka_state == NONE */ /* send keep-alive */
        struct pptp_echo_rqst rqst = {
            PPTP_HEADER_CTRL(PPTP_ECHO_RQST), hton32(global.conn->ka_id) };
        pptp_send_ctrl_packet(global.conn, &rqst, sizeof(rqst));
        global.conn->ka_state = KA_OUTSTANDING;
        /* XXX FIXME: wake up ctrl thread -- or will the SIGALRM do that
         * automagically? XXX
         */
    }
    /* check incoming/outgoing call states for !IDLE && !ESTABLISHED */
    for (i = 0; i < vector_size(global.conn->call); i++) {
        PPTP_CALL * call = vector_get_Nth(global.conn->call, i);
        if (call->call_type == PPTP_CALL_PNS) {
            if (call->state.pns == PNS_WAIT_REPLY) {
                /* send close request */
                pptp_call_close(global.conn, call);
                assert(call->state.pns == PNS_WAIT_DISCONNECT);
            } else if (call->state.pns == PNS_WAIT_DISCONNECT) {
                /* hard-close the call */
                pptp_call_destroy(global.conn, call);
            }
        } else if (call->call_type == PPTP_CALL_PAC) {
            if (call->state.pac == PAC_WAIT_REPLY) {
                /* XXX FIXME -- drop the PAC connection XXX */
            } else if (call->state.pac == PAC_WAIT_CS_ANS) {
                /* XXX FIXME -- drop the PAC connection XXX */
            }
        }
    }
    pptp_reset_timer();
}
Esempio n. 3
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;
}
Esempio n. 4
0
/* Call Manager */
int 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;
  int first=1;
  int retval;
  int i;

  openlog("pptp_callmgr", LOG_PID, 0);

  /* Step 0: Check arguments */
  if (argc!=2){
    logmsg("Usage: %s ip.add.ress.here", argv[0]);
	exit(0);
  }
  if (inet_aton(argv[1], &inetaddr)==0){
    logmsg("Invalid IP address: %s", argv[1]);
	exit(0);
  }

  /*
  We need to connect to the unxisocket first becasue of the way this program
  is launched.  We need to check if we can bind to the Unix socket to see if we 
  have already started.
  */
  if ((unix_sock = open_unixsock(inetaddr)) < 0){
    logmsg("Could not open unix socket for %s", argv[1]);
	exit(0); /*Exit because there is nothing yet to be done in here*/
  }

  if ((inet_sock = open_inetsock(inetaddr)) < 0){  /* Step 1: Open sockets. */
    logmsg("Could not open control connection to %s", argv[1]);
	close_unixsock(unix_sock, inetaddr);
	sleep(2); /* so we don't respawn too quickly and annoy init */
	exit(0);
  }

#if 0 /* we are already an execing program if 2 exec model chosen */
  /* Step 1b: FORK and return status to calling process. */
  switch (fork()) {
  case 0: /* child. stick around. */
    break;
  default: /* Parent. Return status to caller. */
    exit(0);
  }
#endif

  /* Step 1c: Clean up unix socket on TERM */
  signal(SIGINT, sighandler);
  signal(SIGTERM, sighandler);
  signal(SIGKILL, sighandler);
  if (sigsetjmp(env, 1)!=0) goto cleanup;
  signal(SIGPIPE, do_nothing);
  signal(SIGUSR1, 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) {
    logmsg("Could not open connection.");
	sleep(2); /* so we don't respawn too quickly and annoy init */
	goto cleanup;
  }

  pptp_debug("Call Mgr Started for %s", argv[1]);

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

  if (sigsetjmp(env, 1)!=0) { pptp_debug("signalled!"); goto shutdown; }

  setpgrp(); /* stop our dad from killing us */

  /* Step 3: Get FD_SETs */
  do {
    fd_set read_set, write_set, excpt_set = call_set;
    FD_ZERO(&read_set);
    FD_ZERO(&write_set);
	read_set = call_set; /* need to remember the calls */
	if (pptp_conn_established(conn) && unix_sock != -1)
      FD_SET(unix_sock, &read_set);
    pptp_fd_set(conn, &read_set, &write_set);

    /* Step 4: Wait on INET or UNIX event */
    if (select(max_fd+1, &read_set, &write_set, &excpt_set, NULL)<0) {
	  if (errno != EINTR) {
		logmsg("select: %s", strerror(errno));
	  }
	  sleep(1); /* hope the error goes away,  but be nice to the host also */
      /* 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 (unix_sock >= 0 && 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 */
      if ((s = accept(unix_sock, (struct sockaddr *) &from, &len))<0) {
		logmsg("Socket not accepted: %s", strerror(errno));
		goto skip_accept;
      }
	  pptp_debug("accepting unixsock %d", s);
      /* Allocate memory for local call information structure. */
      if ((lci = malloc(sizeof(*lci))) == NULL) {
		logmsg("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);
	  if (call == NULL){
	    logmsg("Couldn't allocate new call");
	    close(s);
	    goto skip_accept;
	  }
      /* Read and store the associated pids */
	  pptp_debug("Waiting to read the pids");
      read(s, &lci->pid[0], sizeof(lci->pid[0]));
	  pptp_debug("pids read!!");
      /* 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:
  	if (pptp_conn_down(conn)) {
      logmsg("conn down!");
	  goto shutdown;
	}
    /* Step 5c: Handle socket close */
    for (i=0; i<=max_fd; i++) {
      if (FD_ISSET(i, &call_set) &&
	  		(FD_ISSET(i, &excpt_set) || FD_ISSET(i, &read_set))) {
	/* close it */
	PPTP_CALL * call;

	pptp_debug("exception/data on fd %d", i);
	retval = vector_search(call_list, i, &call);
	if (retval) {
#if 1
	  struct local_callinfo *lci = pptp_call_closure_get(conn, call);
	  if (lci) {
	  kill(lci->pid[0], SIGTERM);
      memset(lci, 0, sizeof(*lci));
	  free(lci);
	  }
	  pptp_call_closure_put(conn, call, NULL);
	  vector_remove(call_list, i);
#endif
	  /* soft shutdown.  Callback will do hard shutdown later */
	  pptp_call_close(conn, call);
	}
	FD_CLR(i, &call_set);
	pptp_debug("closing unixsock %d", i);
	close(i);
      }
	}
  } while (!pptp_conn_down(conn) && (vector_size(call_list)>0 || first));

  logmsg("Callmanager has no more calls ! exiting");
shutdown:
  pptp_debug("Shutdown");
  /* make sure we clean up properly if interrupted during shutdown */
  signal(SIGINT, sighandler);
  signal(SIGTERM, sighandler);
  signal(SIGKILL, sighandler);
  if (sigsetjmp(env, 1)!=0) goto cleanup;

  if (!pptp_conn_down(conn)) {
    fd_set read_set, write_set;

    pptp_debug("Shutdown cleanly");
    /* 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);
	  if (lci) {
      kill(lci->pid[0], SIGTERM);
	  }
      pptp_call_close(conn, call);
    }
    /* attempt to dispatch these messages */
    FD_ZERO(&read_set);
    FD_ZERO(&write_set);
    pptp_fd_set(conn, &read_set, &write_set);
    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);
    FD_ZERO(&read_set);
    pptp_dispatch(conn, &read_set, &write_set);
    sleep(2);
    /* with extreme prejudice */
    pptp_conn_destroy(conn, 1);
    vector_destroy(call_list);
  }

cleanup:
  close_inetsock(inet_sock, inetaddr);
  inet_sock = -1;
  close_unixsock(unix_sock, inetaddr);
  unix_sock = -1;
  pptp_debug("Gone");
  return 0;
}