Beispiel #1
0
/* XXX need better error checking XXX */
int get_call_id(int sock, pid_t gre,
		 u_int16_t *call_id, u_int16_t *peer_call_id) {
  u_int16_t m_call_id, m_peer_call_id;
  /* write pid's to socket */
  /* don't bother with network byte order, because pid's are meaningless
   * outside the local host.
   */
  int rc;
  rc = write(sock, (char *)&gre, sizeof(gre));
  if (rc != sizeof(gre))
	  return -1;
  pptp_debug("wrote socket information, waiting for read...");
  rc = read(sock,  (char *)&m_call_id, sizeof(m_call_id));
  if (rc != sizeof(m_call_id))
	  return -1;
  rc = read(sock,  (char *)&m_peer_call_id, sizeof(m_peer_call_id));
  if (rc != sizeof(m_peer_call_id))
	  return -1;
  pptp_debug("Read socket information: call_id=%d, peer_call_id=%d",
		  m_call_id, m_peer_call_id);
  /* XXX FIX ME ... DO ERROR CHECKING & TIME-OUTS XXX */
  *call_id = m_call_id;
  *peer_call_id = m_peer_call_id;

  return 0;
}
Beispiel #2
0
/*
 * search through a possible list of ',' seperated ip addresses, try
 * each one,  if it works then use that one
 */
struct in_addr get_ip_address(char *name) {
  struct in_addr retval;
  struct sockaddr_in dest;
  int s;
  char *cp, *np;

  retval.s_addr = 0;
  for (cp = name; cp && *cp; cp = np) {

    if (np = strchr(cp, ','))
    	*np++ = '\0';
    pptp_error("Trying host %s ...", cp);
    if (inet_aton(cp, &retval) == 0) {
      struct hostent *host = gethostbyname(cp);
      if (host==NULL) {
	    if (h_errno == HOST_NOT_FOUND)
	      pptp_error("gethostbyname: HOST NOT FOUND");
	    else if (h_errno == NO_ADDRESS)
	      pptp_error("gethostbyname: NO IP ADDRESS");
	    else
	      pptp_error("gethostbyname: name server error");
	    continue;
      }
      if (host->h_addrtype != AF_INET) {
	    pptp_error("Host has non-internet address");
	    continue;
	  }
      memcpy(&retval.s_addr, host->h_addr, sizeof(retval.s_addr));
    }

    if (np)
    	*(np - 1) = ','; /* put string back how we found it */

    bzero(&dest, sizeof(dest));
    dest.sin_family = AF_INET;
    dest.sin_port   = htons(PPTP_PORT);
    dest.sin_addr   = retval;
    pptp_debug("socket");
    if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
      pptp_error("Cannot get socket: %s", strerror(errno));
      continue;
    }
    pptp_debug("Connect");
    signal(SIGALRM, do_alarm);
    alarm(15);
    if (connect(s, (struct sockaddr *) &dest, sizeof(dest)) != -1) {
      alarm(0);
      pptp_error("Connect succeeded");
      close(s);
      return(retval);
    }
    alarm(0);
	close(s);
    pptp_error("Connect failed: %s",strerror(errno));
  }
  retval.s_addr = 0;
  return retval;
}
Beispiel #3
0
/* Call callback */
void call_callback(PPTP_CONN *conn, PPTP_CALL *call, enum call_state state) {
  struct local_callinfo *lci;
  struct local_conninfo *conninfo;
  u_int16_t call_id[2];

  switch(state) {
  case CALL_OPEN_DONE:
    /* okey dokey.  This means that the call_id and peer_call_id are now
     * valid, so lets send them on to our friends who requested this call.
     */
	pptp_debug("About to get the call closure stuff");
    lci = pptp_call_closure_get(conn, call); assert(lci != NULL);
    pptp_call_get_ids(conn, call, &call_id[0], &call_id[1]);
	pptp_debug("writing out the call_ids");
	if (lci)
    write(lci->unix_sock, (char *) &call_id, sizeof(call_id));
    /* Our duty to the fatherland is now complete. */
    break;
  case CALL_OPEN_FAIL:
  case CALL_CLOSE_RQST:
  case CALL_CLOSE_DONE:
    /* don't need to do anything here, except make sure tables are sync'ed */
    conninfo = pptp_conn_closure_get(conn);
    lci = pptp_call_closure_get(conn, call); 
    // assert(lci != NULL && conninfo != NULL);
    if (lci && conninfo &&
		vector_contains(conninfo->call_list, lci->unix_sock)) {
      vector_remove(conninfo->call_list, lci->unix_sock);
	  if (lci->unix_sock >= 0)
        FD_CLR(lci->unix_sock, conninfo->call_set);
//	  syslog(LOG_NOTICE, "Closing connection");
      kill(lci->pid[0], SIGTERM);
    }
	if (lci) {
	  pptp_debug("close %d", lci->unix_sock);
      close(lci->unix_sock);
	  lci->unix_sock = -1;
#if 0
	  memset(lci, 0, sizeof(*lci));
	  free(lci);
	  pptp_call_closure_put(conn, call, NULL); 
#endif
	}
    break;
  default:
    logmsg("Unhandled call callback state [%d].", (int) state);
    break;
  }
}
Beispiel #4
0
void close_unixsock(int fd, struct in_addr inetaddr) {
  struct sockaddr_un where;
  close(fd);
  snprintf(where.sun_path, sizeof(where.sun_path), 
	   PPTP_SOCKET_PREFIX "%s", inet_ntoa(inetaddr));
  unlink(where.sun_path);
  pptp_debug("unlink %s", where.sun_path);
}
Beispiel #5
0
int open_unixsock(struct in_addr inetaddr) {
  struct sockaddr_un where;
  struct stat st;
  char *dir;
  int s;
  char *call_id_str = getenv("pptp_call_id");

  if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
    logmsg("socket: %s", strerror(errno));
    return s;
  }

  where.sun_family = AF_UNIX;
  snprintf(where.sun_path, sizeof(where.sun_path), 
	   PPTP_SOCKET_PREFIX "%s%s%s", inet_ntoa(inetaddr),
	   call_id_str ? "." : "", call_id_str ? call_id_str : "");

  if (stat(where.sun_path, &st) >= 0) {
    logmsg("Call manager for %s is already running.", inet_ntoa(inetaddr));
    close(s); return -1;
  }

  /* Make sure path is valid. */
  dir = dirname(where.sun_path);
  if (!make_valid_path(dir, 0770))
    logmsg("Could not make path to %s: %s", where.sun_path, strerror(errno));
  free(dir);

  if (bind(s, (struct sockaddr *) &where, sizeof(where)) < 0) {
    logmsg("bind: %s", strerror(errno));
    close(s); return -1;
  }
  pptp_debug("Bound unix domain socket: %s", where.sun_path);

  chmod(where.sun_path, 0777);

  listen(s, 127);

  return s;
}
Beispiel #6
0
void do_nothing(int sig) {
  /* do nothing signal handler */
  pptp_debug("Got nothing-signal %d", sig);
}
Beispiel #7
0
void sighandler(int sig) {
  pptp_debug("Got handled-signal %d", sig);
  siglongjmp(env, 1);
}
Beispiel #8
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;
}
Beispiel #9
0
int main(int argc, char **argv, char **envp) {
  struct in_addr inetaddr;
  int callmgr_sock = -1;
  char ptydev[PTYMAX], ttydev[TTYMAX];
  int pty_fd = -1;
  int gre_fd = -1;
  static volatile pid_t child_pid;
  u_int16_t call_id, peer_call_id;
  
#ifdef EMBED
  openlog(argv[0],LOG_PID,LOG_USER);
#endif

  if (argc < 2)
    usage(argv[0]);

  /* Step 1: Get IP address for the hostname in argv[1] */
  for (;;) {
    inetaddr = get_ip_address(argv[1]);
    if(inetaddr.s_addr != 0)
      break;
    sleep(RESPAWN_DELAY);
  }

  /*
   * open the GRE socket early so that we do not get
   * ENOPROTOOPT errors if the other end responds too
   * quickly to our initial connection
   */
  gre_fd = socket(AF_INET, SOCK_RAW, PPTP_PROTO);
  if (gre_fd < 0) {
	pptp_error("socket: %s\n", strerror(errno));
    sleep(RESPAWN_DELAY);
	exit(1);
  }

  for (;;) {
    /* Step 2: Open connection to call manager
	 *         (Launch call manager if necessary.)
	 */
	callmgr_sock = open_callmgr(inetaddr, argc,argv,envp);
	if(callmgr_sock < 0){
      close(gre_fd);
	  pptp_error("Could not open connection to call manager - terminating");
	  sleep(RESPAWN_DELAY);
	  exit(1);
	}
	pptp_debug("callmgr opened - fd = %x", callmgr_sock);

	/* Step 5: Exchange PIDs, get call ID */
	if (get_call_id(callmgr_sock, getpid(), &call_id, &peer_call_id) >= 0)
		break;

	close(callmgr_sock);
  }

  /* Step 3: Find an open pty/tty pair. */
  pty_fd = getpseudotty(ttydev, ptydev);
  if (pty_fd < 0) {
    close(gre_fd);
    close(callmgr_sock);
	pptp_error("Could not find free pty.");
	sleep(RESPAWN_DELAY);
	exit(1);
  }
  pptp_debug("got a free ttydev");
  
  /* Step 4: fork and wait. */
  signal(SIGUSR1, do_nothing); /* don't die */
  switch (child_pid = vfork()) {
  case -1:
    signal(SIGUSR1, SIG_DFL);
    pptp_debug("vfork failed %s", strerror(errno));
	sleep(RESPAWN_DELAY);
	goto shutdown;
  case 0: /* I'm the child! */
//    signal(SIGUSR1, SIG_DFL);
	pptp_debug("entered child");
    pptp_debug("callids established..");
	close(callmgr_sock);
	close(gre_fd);
	close(pty_fd);
    launch_pppd(ttydev, argc-2, argv+2); /* launch pppd */
	sleep(RESPAWN_DELAY);
	exit(1); /* in case launch_pppd returns */
    break;
  default: /* parent */
    /*
     * 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 0
	if (!signaled) {
	  pause(); /* wait for the signal */
    }
    pptp_error("Error %s", strerror(errno));
#endif /*0*/
    break;
  }

#if 0
  /* Step 5b: Send signal to wake up pppd task */
  kill(parent_pid, SIGUSR1);
  sleep(2);
#endif /*0*/

  if (sigsetjmp(env, 1)!=0) goto shutdown;
  signal(SIGINT,  sighandler);
  signal(SIGTERM, sighandler);
  signal(SIGKILL, sighandler);

  {
    char buf[128];
    snprintf(buf, sizeof(buf), "pptp: GRE-to-PPP gateway on %s", ptydev);
    inststr(argc,argv,envp, buf);
  }

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

shutdown:
  /* Make sure pppd exits as well */
  if (child_pid > 0)
    kill(child_pid, SIGTERM);
  if (gre_fd != -1)
    close(gre_fd);
  if (pty_fd != -1)
    close(pty_fd);
  if (callmgr_sock != -1)
    close(callmgr_sock);
  exit(0);
}