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; }
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 }