int qcs_waitinput(
	qcs_link link_id,
	int timeout_ms )
{
	link_data * link = (link_data *) link_id;
	struct timeval tv;
	fd_set fds;

	// check if link is valid
	if(!VALID_ID(link_id)) ERRRET(EINVAL);
	if(!ACTIVE_LINK(link)) ERRRET(EINVAL);

	// fill structs & select()
	if(timeout_ms < 0) {
		tv.tv_sec = 0;
		tv.tv_usec = 0;
	} else {
		tv.tv_sec = timeout_ms / 1000;
		tv.tv_usec = (timeout_ms % 1000) * 1000;
	}

	FD_ZERO(&fds);
	FD_SET(link->rx, &fds);

	return select(FD_SETSIZE, &fds, NULL, NULL, &tv);
}
int qcs_close(qcs_link link_id)
{
	link_data * link = (link_data *)link_id;

	/* check if link is valid */
	if( !VALID_ID(link_id)) {
		ERRRET(EINVAL);
	}

	if(!ACTIVE_LINK(link)) {
		ERRRET(EINVAL);
	}

	/* shutdown sockets */
	close(link->rx);
	close(link->tx);

	/* delete broadcast ip list */
	free(link->broadcasts);

	/* delete link entry */
	free(link);

	link_count--;
	if( link_count==0 ) {
		/* clean up duplicate buffer */
		qcs__cleanup_dup();
	}

	return 1;
}
int qcs_recv(
	qcs_link link_id,
	qcs_msg * msg )
{
	link_data * link = (link_data *)link_id;
	char * buff;

	struct sockaddr_in sa;
	socklen_t sa_len;
	int retval;

	if(msg==NULL) ERRRET(EINVAL);

	if(!VALID_ID(link_id)) ERRRET(EINVAL);
	if(!ACTIVE_LINK(link)) ERRRET(EINVAL);

	buff = (char*) malloc(QCP_MAXUDPSIZE+0x10);
	if(buff==NULL) ERRRET(ENOMEM);

	// recv the data
	sa_len = sizeof(sa);
	retval = recvfrom(
		link->rx, (void*)buff, QCP_MAXUDPSIZE, 0,
		(struct sockaddr*)&sa, &sa_len
	);
	
	/* check if msg is too long for us to process:
	 * just strip it down. we guess this was the last
	 * ascii field, in proto msg, that was soo long.
	 */
	if(retval==QCP_MAXUDPSIZE) {
		*(char*)(buff+QCP_MAXUDPSIZE-1)='\0';
	}

	/* failure */
	if(retval < 0) {
		free(buff);
		/* errno left from recvfrom() */
		return 0;
	}

	/* parse the message */
	switch(link->mode)
	{
	case QCS_PROTO_QCHAT:
		retval = qcs__parse_qchat_msg(buff, retval, msg);
		break;
	case QCS_PROTO_VYPRESS:
		retval = qcs__parse_vypress_msg(buff, retval, msg);
		break;
	}

	/* cleanup */
	free((void*)buff);
	return retval;
}
int qcs_rxsocket(
	qcs_link link_id,
	int * p_rxsocket )
{
	link_data * link = (link_data*)link_id;

	if(!VALID_ID(link_id)) ERRRET(EINVAL);
	if(!ACTIVE_LINK(link)) ERRRET(EINVAL);

	*p_rxsocket = link->rx;
	return 1;
}
int qcs_send(
	qcs_link link_id,
	const qcs_msg * msg )
{
	const char * proto_msg;
	int proto_len, retval;
	struct sockaddr_in sab;
	link_data * link = (link_data *)link_id;
	int bcast, succ = 0;

	// check link
	if(!VALID_ID(link_id)) ERRRET(EINVAL);
	if(!ACTIVE_LINK(link)) ERRRET(EINVAL);

	// build proto message
	switch(link->mode) {
	case QCS_PROTO_VYPRESS:
		proto_msg = qcs__make_vypress_msg(msg, &proto_len);
		break;
	case QCS_PROTO_QCHAT:
		proto_msg = qcs__make_qchat_msg(msg, &proto_len);
		break;
	}
	if( proto_msg==NULL ) {
		// failed to build msg
		ERRRET(EINVAL);
	}

	sab.sin_family = PF_INET;
	sab.sin_port = htons(link->port);

	/* send msg to every network in bcast list */
	for(bcast=0; bcast < link->broadcast_count; bcast++)
	{
		sab.sin_addr.s_addr = link->broadcasts[bcast];

		retval = sendto(
			link->tx, proto_msg, proto_len, 0,
			(struct sockaddr*)&sab, sizeof(sab));

		/* we return success if we managed to 
		 * send to at least one broadcast address */
		succ |= retval==proto_len;
	}
	free((void*)proto_msg);

	if(!succ) errno = ENETUNREACH;
	return succ;
}
Esempio n. 6
0
int
main(int argc, char *argv[])
{
    int ch, idx, plen, nready, interactive = 0, listonly = 0;
    const char *id, *user = NULL, *pattern = NULL, *tty = NULL, *decimal = ".";
    char path[PATH_MAX], buf[LINE_MAX], *cp, *ep;
    double seconds, to_wait, speed = 1.0, max_wait = 0;
    FILE *lfile;
    fd_set *fdsw;
    sigaction_t sa;
    size_t len, nbytes, nread, off;
    ssize_t nwritten;

#if !defined(HAVE_GETPROGNAME) && !defined(HAVE___PROGNAME)
    setprogname(argc > 0 ? argv[0] : "sudoreplay");
#endif

#ifdef HAVE_SETLOCALE
    setlocale(LC_ALL, "");
    decimal = localeconv()->decimal_point;
#endif

    while ((ch = getopt(argc, argv, "d:f:hlm:s:V")) != -1) {
	switch(ch) {
	case 'd':
	    session_dir = optarg;
	    break;
	case 'f':
	    /* Set the replay filter. */
	    replay_filter = 0;
	    for (cp = strtok(optarg, ","); cp; cp = strtok(NULL, ",")) {
		if (strcmp(cp, "stdout") == 0)
		    SET(replay_filter, 1 << IOFD_STDOUT);
		else if (strcmp(cp, "stderr") == 0)
		    SET(replay_filter, 1 << IOFD_STDERR);
		else if (strcmp(cp, "ttyout") == 0)
		    SET(replay_filter, 1 << IOFD_TTYOUT);
		else
		    errorx(1, "invalid filter option: %s", optarg);
	    }
	    break;
	case 'h':
	    help();
	    /* NOTREACHED */
	case 'l':
	    listonly = 1;
	    break;
	case 'm':
	    errno = 0;
	    max_wait = strtod(optarg, &ep);
	    if (*ep != '\0' || errno != 0)
		errorx(1, "invalid max wait: %s", optarg);
	    break;
	case 's':
	    errno = 0;
	    speed = strtod(optarg, &ep);
	    if (*ep != '\0' || errno != 0)
		errorx(1, "invalid speed factor: %s", optarg);
	    break;
	case 'V':
	    (void) printf("%s version %s\n", getprogname(), PACKAGE_VERSION);
	    exit(0);
	default:
	    usage(1);
	    /* NOTREACHED */
	}

    }
    argc -= optind;
    argv += optind;

    if (listonly)
	exit(list_sessions(argc, argv, pattern, user, tty));

    if (argc != 1)
	usage(1);

    /* 6 digit ID in base 36, e.g. 01G712AB */
    id = argv[0];
    if (!VALID_ID(id))
	errorx(1, "invalid ID %s", id);

    plen = snprintf(path, sizeof(path), "%s/%.2s/%.2s/%.2s/timing",
	session_dir, id, &id[2], &id[4]);
    if (plen <= 0 || plen >= sizeof(path))
	errorx(1, "%s/%.2s/%.2s/%.2s/%.2s/timing: %s", session_dir,
	    id, &id[2], &id[4], strerror(ENAMETOOLONG));
    plen -= 7;

    /* Open files for replay, applying replay filter for the -f flag. */
    for (idx = 0; idx < IOFD_MAX; idx++) {
	if (ISSET(replay_filter, 1 << idx) || idx == IOFD_TIMING) {
	    io_fds[idx].v = open_io_fd(path, plen, io_fnames[idx]);
	    if (io_fds[idx].v == NULL)
		error(1, "unable to open %s", path);
	}
    }

    /* Read log file. */
    path[plen] = '\0';
    strlcat(path, "/log", sizeof(path));
    lfile = fopen(path, "r");
    if (lfile == NULL)
	error(1, "unable to open %s", path);
    cp = NULL;
    len = 0;
    /* Pull out command (third line). */
    if (getline(&cp, &len, lfile) == -1 ||
	getline(&cp, &len, lfile) == -1 ||
	getline(&cp, &len, lfile) == -1) {
	errorx(1, "invalid log file %s", path);
    }
    printf("Replaying sudo session: %s", cp);
    free(cp);
    fclose(lfile);

    fflush(stdout);
    zero_bytes(&sa, sizeof(sa));
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = SA_RESETHAND;
    sa.sa_handler = cleanup;
    (void) sigaction(SIGINT, &sa, NULL);
    (void) sigaction(SIGKILL, &sa, NULL);
    (void) sigaction(SIGTERM, &sa, NULL);
    (void) sigaction(SIGHUP, &sa, NULL);
    sa.sa_flags = SA_RESTART;
    sa.sa_handler = SIG_IGN;
    (void) sigaction(SIGTSTP, &sa, NULL);
    (void) sigaction(SIGQUIT, &sa, NULL);

    /* XXX - read user input from /dev/tty and set STDOUT to raw if not a pipe */
    /* Set stdin to raw mode if it is a tty */
    interactive = isatty(STDIN_FILENO);
    if (interactive) {
	ch = fcntl(STDIN_FILENO, F_GETFL, 0);
	if (ch != -1)
	    (void) fcntl(STDIN_FILENO, F_SETFL, ch | O_NONBLOCK);
	if (!term_raw(STDIN_FILENO, 1))
	    error(1, "cannot set tty to raw mode");
    }
    fdsw = (fd_set *)emalloc2(howmany(STDOUT_FILENO + 1, NFDBITS),
	sizeof(fd_mask));

    /*
     * Timing file consists of line of the format: "%f %d\n"
     */
#ifdef HAVE_ZLIB_H
    while (gzgets(io_fds[IOFD_TIMING].g, buf, sizeof(buf)) != NULL) {
#else
    while (fgets(buf, sizeof(buf), io_fds[IOFD_TIMING].f) != NULL) {
#endif
	if (!parse_timing(buf, decimal, &idx, &seconds, &nbytes))
	    errorx(1, "invalid timing file line: %s", buf);

	if (interactive)
	    check_input(STDIN_FILENO, &speed);

	/* Adjust delay using speed factor and clamp to max_wait */
	to_wait = seconds / speed;
	if (max_wait && to_wait > max_wait)
	    to_wait = max_wait;
	delay(to_wait);

	/* Even if we are not relaying, we still have to delay. */
	if (io_fds[idx].v == NULL)
	    continue;

	/* All output is sent to stdout. */
	while (nbytes != 0) {
	    if (nbytes > sizeof(buf))
		len = sizeof(buf);
	    else
		len = nbytes;
#ifdef HAVE_ZLIB_H
	    nread = gzread(io_fds[idx].g, buf, len);
#else
	    nread = fread(buf, 1, len, io_fds[idx].f);
#endif
	    nbytes -= nread;
	    off = 0;
	    do {
		/* no stdio, must be unbuffered */
		nwritten = write(STDOUT_FILENO, buf + off, nread - off);
		if (nwritten == -1) {
		    if (errno == EINTR)
			continue;
		    if (errno == EAGAIN) {
			FD_SET(STDOUT_FILENO, fdsw);
			do {
			    nready = select(STDOUT_FILENO + 1, NULL, fdsw, NULL, NULL);
			} while (nready == -1 && errno == EINTR);
			if (nready == 1)
			    continue;
		    }
		    error(1, "writing to standard output");
		}
		off += nwritten;
	    } while (nread > off);
	}
    }
    term_restore(STDIN_FILENO, 1);
    exit(0);
}

static void
delay(double secs)
{
    struct timespec ts, rts;
    int rval;

    /*
     * Typical max resolution is 1/HZ but we can't portably check that.
     * If the interval is small enough, just ignore it.
     */
    if (secs < 0.0001)
	return;

    rts.tv_sec = secs;
    rts.tv_nsec = (secs - (double) rts.tv_sec) * 1000000000.0;
    do {
      memcpy(&ts, &rts, sizeof(ts));
      rval = nanosleep(&ts, &rts);
    } while (rval == -1 && errno == EINTR);
    if (rval == -1)
	error(1, "nanosleep: tv_sec %ld, tv_nsec %ld", ts.tv_sec, ts.tv_nsec);
}

static void *
open_io_fd(char *path, int len, const char *suffix)
{
    path[len] = '\0';
    strlcat(path, suffix, PATH_MAX);

#ifdef HAVE_ZLIB_H
    return gzopen(path, "r");
#else
    return fopen(path, "r");
#endif
}