// Loop forwarding data from in1 to out1 and in2 to out2, handling // half-connection shutdown. timeouts return if no data for X miliseconds. // Returns 0: both closed, 1 shutdown_timeout, 2 timeout int pollinate(int in1, int in2, int out1, int out2, int timeout, int shutdown_timeout) { struct pollfd pollfds[2]; int i, pollcount = 2; memset(pollfds, 0, 2*sizeof(struct pollfd)); pollfds[0].events = pollfds[1].events = POLLIN; pollfds[0].fd = in1; pollfds[1].fd = in2; // Poll loop copying data from each fd to the other one. for (;;) { if (!xpoll(pollfds, pollcount, timeout)) return pollcount; for (i=0; i<pollcount; i++) { if (pollfds[i].revents & POLLIN) { int len = read(pollfds[i].fd, libbuf, sizeof(libbuf)); if (len<1) pollfds[i].revents = POLLHUP; else xwrite(i ? out2 : out1, libbuf, len); } if (pollfds[i].revents & POLLHUP) { // Close half-connection. This is needed for things like // "echo GET / | netcat landley.net 80" // Note that in1 closing triggers timeout, in2 returns now. if (i) { shutdown(pollfds[0].fd, SHUT_WR); pollcount--; timeout = shutdown_timeout; } else return 0; } } } }
static VALUE loop_blocking_poll(void *argp) { lp_arg *args = argp; args->result = xpoll(args->fds, args->nfd, args->ts); if (args->result < 0) args->lerrno = errno; return Qnil; }
// Scan stdin for a keypress, parsing known escape sequences // Returns: 0-255=literal, -1=EOF, -2=NONE, 256-...=index into seq // scratch space is necessary because last char of !seq could start new seq // Zero out first byte of scratch before first call to scan_key // block=0 allows fetching multiple characters before updating display int scan_key(char *scratch, int block) { // up down right left pgup pgdn home end ins char *seqs[] = {"\033[A", "\033[B", "\033[C", "\033[D", "\033[5~", "\033[6~", "\033OH", "\033OF", "\033[2~", 0}; struct pollfd pfd; int maybe, i, j; char *test; for (;;) { pfd.fd = 0; pfd.events = POLLIN; pfd.revents = 0; // check sequences maybe = 0; if (*scratch) { for (i = maybe = 0; (test = seqs[i]); i++) { for (j = 0; j<*scratch; j++) if (scratch[j+1] != test[j]) break; if (j == *scratch) { maybe = 1; if (!test[j]) { // We recognized current sequence: consume and return *scratch = 0; return 256+i; } } } // If current data can't be a known sequence, return next raw char if (!maybe) break; } // Need more data to decide // 30 miliseconds is about the gap between characters at 300 baud if (maybe || !block) if (!xpoll(&pfd, 1, 30*maybe)) break; if (1 != read(0, scratch+1+*scratch, 1)) return -1; ++*scratch; } // Was not a sequence if (!*scratch) return -2; i = scratch[1]; if (--*scratch) memmove(scratch+1, scratch+2, *scratch); return i; }
/* * Derived from the mcmd() libc call, with modified interface. * This version is MT-safe. Errors are displayed in pdsh-compat format. * Connection can time out. * ahost (IN) target hostname * addr (IN) 4 byte internet address * locuser (IN) local username * remuser (IN) remote username * cmd (IN) remote command to execute under shell * rank (IN) not used * fd2p (IN) if non NULL, return stderr file descriptor here * int (RETURN) -1 on error, socket for I/O on success * * Originally by Mike Haskell for mrsh, modified slightly to work with pdsh by: * - making mcmd always thread safe * - using "err" function output errors. * - passing in address as addr intead of calling gethostbyname * - using default mshell port instead of calling getservbyname * */ static int mcmd(char *ahost, char *addr, char *locuser, char *remuser, char *cmd, int rank, int *fd2p, void **argp) { struct sockaddr m_socket; struct sockaddr_in *getp; struct sockaddr_in sin, from; struct sockaddr_storage ss; struct in_addr m_in; unsigned int rand, randl; unsigned char *hptr; int s, s2, rv, mcount, lport; char c; char num[6] = {0}; char *mptr; char *mbuf; char *tmbuf; char *m; char *mpvers; char num_seq[12] = {0}; socklen_t len; sigset_t blockme; sigset_t oldset; char haddrdot[MAXHOSTNAMELEN + MRSH_LOCALHOST_KEYLEN + 1] = {0}; munge_ctx_t ctx; struct xpollfd xpfds[2]; memset (xpfds, 0, sizeof (xpfds)); memset (&sin, 0, sizeof (sin)); sigemptyset(&blockme); sigaddset(&blockme, SIGURG); sigaddset(&blockme, SIGPIPE); SET_PTHREAD(); /* Convert randy to decimal string, 0 if we dont' want stderr */ if (fd2p != NULL) snprintf(num_seq, sizeof(num_seq),"%d",randy); else snprintf(num_seq, sizeof(num_seq),"%d",0); /* * Start setup of the stdin/stdout socket... */ lport = 0; len = sizeof(struct sockaddr_in); if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) { err("%p: %S: mcmd: socket call stdout failed: %m\n", ahost); EXIT_PTHREAD(); } memset (&ss, '\0', sizeof(ss)); ss.ss_family = AF_INET; if (bind(s, (struct sockaddr *)&ss, len) < 0) { err("%p: %S: mcmd: bind failed: %m\n", ahost); goto bad; } sin.sin_family = AF_INET; memcpy(&sin.sin_addr.s_addr, addr, IP_ADDR_LEN); sin.sin_port = htons(MRSH_PORT); if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) < 0) { err("%p: %S: mcmd: connect failed: %m\n", ahost); goto bad; } lport = 0; s2 = -1; if (fd2p != NULL) { /* * Start the socket setup for the stderr. */ struct sockaddr_in sin2; if ((s2 = socket(AF_INET, SOCK_STREAM, 0)) < 0) { err("%p: %S: mcmd: socket call for stderr failed: %m\n", ahost); goto bad; } memset (&sin2, 0, sizeof(sin2)); sin2.sin_family = AF_INET; sin2.sin_addr.s_addr = htonl(INADDR_ANY); sin2.sin_port = 0; if (bind(s2,(struct sockaddr *)&sin2, sizeof(sin2)) < 0) { err("%p: %S: mcmd: bind failed: %m\n", ahost); close(s2); goto bad; } len = sizeof(struct sockaddr); /* * Retrieve our port number so we can hand it to the server * for the return (stderr) connection... */ /* getsockname is thread safe */ if (getsockname(s2,&m_socket,&len) < 0) { err("%p: %S: mcmd: getsockname failed: %m\n", ahost); close(s2); goto bad; } getp = (struct sockaddr_in *)&m_socket; lport = ntohs(getp->sin_port); if (listen(s2, 5) < 0) { err("%p: %S: mcmd: listen() failed: %m\n", ahost); close(s2); goto bad; } } /* put port in buffer. will be 0 if user didn't want stderr */ snprintf(num,sizeof(num),"%d",lport); /* * Use special keyed string if target is localhost, otherwise, * encode the IP addr string. */ if (!encode_localhost_string (ahost, haddrdot, sizeof (haddrdot))) { /* inet_ntoa is not thread safe, so we use the following, * which is more or less ripped from glibc */ memcpy(&m_in.s_addr, addr, IP_ADDR_LEN); hptr = (unsigned char *)&m_in; sprintf(haddrdot, "%u.%u.%u.%u", hptr[0], hptr[1], hptr[2], hptr[3]); } /* * We call munge_encode which will take what we write in and return a * pointer to an munged buffer. What we get back is a null terminated * string of encrypted characters. * * The format of the unmunged buffer is as follows (each a string terminated * with a '\0' (null): * * stderr_port_number & /dev/urandom_client_produce_number are 0 * if user did not request stderr socket * SIZE EXAMPLE * ========== ============= * remote_user_name variable "mhaskell" * '\0' * protocol version < 12 bytes "1.2" * '\0' * dotted_decimal_address_of_this_server 7-15 bytes "134.9.11.155" * '\0' * stderr_port_number 4-8 bytes "50111" * '\0' * /dev/urandom_client_produced_number 1-8 bytes "1f79ca0e" * '\0' * users_command variable "ls -al" * '\0' '\0' * * (The last extra null is accounted for in the following line's * last strlen() call.) * */ mpvers = MRSH_PROTOCOL_VERSION; mcount = ((strlen(remuser)+1) + (strlen(mpvers)+1) + (strlen(haddrdot)+1) + (strlen(num)+1) + (strlen(num_seq)+1) + strlen(cmd)+2); tmbuf = mbuf = malloc(mcount); if (tmbuf == NULL) { err("%p: %S: mcmd: Error from malloc\n", ahost); close(s2); goto bad; } /* * The following memset() call takes the extra trailing null as * part of its count as well. */ memset(mbuf,0,mcount); mptr = strcpy(mbuf, remuser); mptr += strlen(remuser)+1; mptr = strcpy(mptr, mpvers); mptr += strlen(mpvers)+1; mptr = strcpy(mptr, haddrdot); mptr += strlen(haddrdot)+1; mptr = strcpy(mptr, num); mptr += strlen(num)+1; mptr = strcpy(mptr, num_seq); mptr += strlen(num_seq)+1; mptr = strcpy(mptr, cmd); ctx = munge_ctx_create(); if ((rv = munge_encode(&m,ctx,mbuf,mcount)) != EMUNGE_SUCCESS) { err("%p: %S: mcmd: munge_encode: %s\n", ahost, munge_ctx_strerror(ctx)); munge_ctx_destroy(ctx); if (s2 >= 0) close(s2); free(tmbuf); goto bad; } munge_ctx_destroy(ctx); mcount = (strlen(m)+1); /* * Write stderr port in the clear in case we can't decode for * some reason (i.e. bad credentials). May be 0 if user * doesn't want stderr */ if (fd2p != NULL) { rv = fd_write_n(s, num, strlen(num)+1); if (rv != (strlen(num) + 1)) { free(m); free(tmbuf); if (errno == EPIPE) err("%p: %S: mcmd: Lost connection (EPIPE): %m", ahost); else err("%p: %S: mcmd: Write of stderr port failed: %m\n", ahost); close(s2); goto bad; } } else { write(s, "", 1); lport = 0; } /* * Write the munge_encoded blob to the socket. */ rv = fd_write_n(s, m, mcount); if (rv != mcount) { free(m); free(tmbuf); if (errno == EPIPE) err("%p: %S: mcmd: Lost connection: %m\n", ahost); else err("%p: %S: mcmd: Write to socket failed: %m\n", ahost); close(s2); goto bad; } free(m); free(tmbuf); if (fd2p != NULL) { /* * Wait for stderr connection from daemon. */ int s3; errno = 0; xpfds[0].fd = s; xpfds[1].fd = s2; xpfds[0].events = xpfds[1].events = XPOLLREAD; if ( ((rv = xpoll(xpfds, 2, -1)) < 0) || rv != 1 || (xpfds[0].revents > 0)) { if (errno != 0) err("%p: %S: mcmd: xpoll (setting up stderr): %m\n", ahost); else err("%p: %S: mcmd: xpoll: protocol failure in circuit setup\n", ahost); (void) close(s2); goto bad; } errno = 0; len = sizeof(from); /* arg to accept */ if ((s3 = accept(s2, (struct sockaddr *)&from, &len)) < 0) { err("%p: %S: mcmd: accept (stderr) failed: %m\n", ahost); close(s2); goto bad; } if (from.sin_family != AF_INET) { err("%p: %S: mcmd: bad family type: %d\n", ahost, from.sin_family); goto bad2; } close(s2); /* * The following fixes a race condition between the daemon * and the client. The daemon is waiting for a null to * proceed. We do this to make sure that we have our * socket is up prior to the daemon running the command. */ if (write(s,"",1) < 0) { err("%p: %S: mcmd: Could not communicate to daemon to proceed: %m\n", ahost); close(s3); goto bad; } /* * Read from our stderr. The server should have placed our * random number we generated onto this socket. */ rv = fd_read_n(s3, &rand, sizeof(rand)); if (rv != (ssize_t) (sizeof(rand))) { err("%p: %S: mcmd: Bad read of expected verification " "number off of stderr socket: %m\n", ahost); close(s3); goto bad; } randl = ntohl(rand); if (randl != randy) { char tmpbuf[LINEBUFSIZE] = {0}; char *tptr = &tmpbuf[0]; memcpy(tptr,(char *) &rand,sizeof(rand)); tptr += sizeof(rand); if (fd_read_line (s3, tptr, LINEBUFSIZE) < 0) err("%p: %S: mcmd: Read error from remote host: %m\n", ahost); else err("%p: %S: mcmd: Error: %s\n", ahost, &tmpbuf[0]); close(s3); goto bad; } /* * Set the stderr file descriptor for the user... */ *fd2p = s3; } if ((rv = read(s, &c, 1)) < 0) { err("%p: %S: mcmd: read: protocol failure: %m\n", ahost); goto bad2; } if (rv != 1) { err("%p: %S: mcmd: read: protocol failure: invalid response\n", ahost); goto bad2; } if (c != '\0') { /* retrieve error string from remote server */ char tmpbuf[LINEBUFSIZE]; if (fd_read_line (s, &tmpbuf[0], LINEBUFSIZE) < 0) err("%p: %S: mcmd: Error from remote host\n", ahost); else err("%p: %S: mcmd: Error: %s\n", ahost, tmpbuf); goto bad2; } RESTORE_PTHREAD(); return (s); bad2: if (lport) close(*fd2p); bad: close(s); EXIT_PTHREAD(); }
// Scan stdin for a keypress, parsing known escape sequences // Blocks for miliwait miliseconds, none 0, forever if -1 // Returns: 0-255=literal, -1=EOF, -2=NONE, 256-...=index into scan_key_list // >512 is x<<9+y<<21 // scratch space is necessary because last char of !seq could start new seq // Zero out first byte of scratch before first call to scan_key // block=0 allows fetching multiple characters before updating display int scan_key(char *scratch, int miliwait) { struct pollfd pfd; int maybe, i, j; char *test; for (;;) { pfd.fd = 0; pfd.events = POLLIN; pfd.revents = 0; maybe = 0; if (*scratch) { int pos[6]; unsigned x, y; // Check for return from terminal size probe memset(pos, 0, 6*sizeof(int)); scratch[(1+*scratch)&15] = 0; sscanf(scratch+1, "\033%n[%n%3u%n;%n%3u%nR%n", pos, pos+1, &y, pos+2, pos+3, &x, pos+4, pos+5); if (pos[5]) { // Recognized X/Y position, consume and return *scratch = 0; return 512+(x<<10)+(y<<20); } else for (i=0; i<6; i++) if (pos[i]==*scratch) maybe = 1; // Check sequences for (i = 0; i<ARRAY_LEN(scan_key_list); i++) { test = scan_key_list[i].seq; for (j = 0; j<*scratch; j++) if (scratch[j+1] != test[j]) break; if (j == *scratch) { maybe = 1; if (!test[j]) { // We recognized current sequence: consume and return *scratch = 0; return 256+i; } } } // If current data can't be a known sequence, return next raw char if (!maybe) break; } // Need more data to decide // 30 miliseconds is about the gap between characters at 300 baud if (maybe || miliwait != -1) if (!xpoll(&pfd, 1, maybe ? 30 : miliwait)) break; // Read 1 byte so we don't overshoot sequence match. (We can deviate // and fail to match, but match consumes entire buffer.) if (toys.signal || 1 != read(0, scratch+1+*scratch, 1)) return toys.signal ? -3 : -1; ++*scratch; } // Was not a sequence if (!*scratch) return -2; i = scratch[1]; if (--*scratch) memmove(scratch+1, scratch+2, *scratch); return i; }
static VALUE loop_run_poll(VALUE argp) { lp_arg *args = (lp_arg*)argp; rb_mt_loop *loop = args->loop; hrtime_t now, next_time; if (loop->events.count) { uint32_t i; args->fds = calloc(loop->events.count, sizeof(struct pollfd)); if (args->fds == NULL) { rb_raise(cb_eClientNoMemoryError, "failed to allocate memory for pollfd"); } for(i = 0; i < loop->events.count; i++) { rb_mt_socket_list *list = &loop->events.sockets[i]; args->fds[i].fd = list->socket; args->fds[i].events = (list->flags & LCB_READ_EVENT ? POLLIN : 0) | (list->flags & LCB_WRITE_EVENT ? POLLOUT : 0); } args->nfd = loop->events.count; } retry: next_time = timers_minimum(&loop->timers); if (next_time) { now = gethrtime(); args->ts = next_time <= now ? 0 : next_time - now; } else { args->ts = HRTIME_INFINITY; } #ifdef HAVE_RB_THREAD_BLOCKING_REGION rb_thread_blocking_region(loop_blocking_poll, args, RUBY_UBF_PROCESS, NULL); #else if (rb_thread_alone()) { TRAP_BEG; args->result = xpoll(args->fds, args->nfd, args->ts); if (args->result < 0) args->lerrno = errno; TRAP_END; } else { /* 5 millisecond pause */ hrtime_t mini_pause = 5000000; int exact = 0; if (args->ts != HRTIME_INFINITY && args->ts < mini_pause) { mini_pause = args->ts; exact = 1; } TRAP_BEG; args->result = xpoll(args->fds, args->nfd, mini_pause); if (args->result < 0) args->lerrno = errno; TRAP_END; if (args->result == 0 && !exact) { args->result = -1; args->lerrno = EINTR; } } #endif if (args->result < 0) { errno = args->lerrno; switch (errno) { case EINTR: #ifdef ERESTART case ERESTART: #endif #ifndef HAVE_RB_THREAD_BLOCKING_REGION rb_thread_schedule(); #endif goto retry; } rb_sys_fail("poll"); return Qnil; } if (next_time) { now = gethrtime(); } if (args->result > 0) { uint32_t cnt = args->result; uint32_t fd_n = 0, ev_n = 0; while (cnt && fd_n < args->nfd && ev_n < loop->events.count) { struct pollfd *res = args->fds + fd_n; rb_mt_socket_list *list = loop->events.sockets + ev_n; rb_mt_event *sock = list->first; /* if plugin used correctly, this checks are noop */ if (res->fd < list->socket) { fd_n++; continue; } else if (res->fd > list->socket) { ev_n++; continue; } if (res->revents) { short flags = ((res->revents & POLLIN_SET) ? LCB_READ_EVENT : 0) | ((res->revents & POLLOUT_SET) ? LCB_WRITE_EVENT : 0); cnt--; loop_enque_events(&loop->callbacks, sock, flags); } fd_n++; ev_n++; } callbacks_run(&loop->callbacks); } if (next_time) { timers_run(&loop->timers, now); } if (loop->events.count == 0 && loop->timers.count == 0) { loop->run = 0; } return Qnil; }
/* * Derived from the rcmd() libc call, with modified interface. * This version is MT-safe. Errors are displayed in pdsh-compat format. * Connection can time out. * ahost (IN) target hostname * locuser (IN) local username * remuser (IN) remote username * cmd (IN) remote command to execute under shell * nodeid (IN) node index for this connection * fd2p (IN) if non NULL, return stderr file descriptor here * int (RETURN) -1 on error, socket for I/O on success */ static int qcmd(char *ahost, char *addr, char *locuser, char *remuser, char *cmd, int nodeid, int *fd2p, void **arg) { struct sockaddr_in sin, from; sigset_t oldset, blockme; pid_t pid; int s, lport, timo, rv; char c; struct xpollfd xpfds[2]; pid = getpid(); sigemptyset(&blockme); sigaddset(&blockme, SIGURG); pthread_sigmask(SIG_BLOCK, &blockme, &oldset); for (timo = 1, lport = IPPORT_RESERVED - 1;;) { s = privsep_rresvport(&lport); if (s < 0) { if (errno == EAGAIN) err("%p: %S: qcmd: socket: all ports in use\n", ahost); else err("%p: %S: qcmd: socket: %m\n", ahost); pthread_sigmask(SIG_SETMASK, &oldset, NULL); return (-1); } fcntl(s, F_SETOWN, pid); sin.sin_family = AF_INET; memcpy(&sin.sin_addr, addr, IP_ADDR_LEN); sin.sin_port = htons(QSHELL_PORT); rv = connect(s, (struct sockaddr *) &sin, sizeof(sin)); if (rv >= 0) break; (void) close(s); if (errno == EADDRINUSE) { lport--; continue; } if (errno == ECONNREFUSED && timo <= 16) { (void) sleep(timo); timo *= 2; continue; } if (errno == EINTR) err("%p: %S: connect: timed out\n", ahost); else err("%p: %S: connect: %m\n", ahost); pthread_sigmask(SIG_SETMASK, &oldset, NULL); return (-1); } lport--; if (fd2p == 0) { write(s, "", 1); lport = 0; } else { char num[8]; int s2 = privsep_rresvport(&lport), s3; socklen_t len = sizeof(from); /* arg to accept */ if (s2 < 0) goto bad; listen(s2, 1); (void) snprintf(num, sizeof(num), "%d", lport); if (write(s, num, strlen(num) + 1) != strlen(num) + 1) { err("%p: %S: qcmd: write (setting up stderr): %m\n", ahost); (void) close(s2); goto bad; } errno = 0; xpfds[0].fd = s; xpfds[1].fd = s2; xpfds[0].events = xpfds[1].events = XPOLLREAD; if (((rv = xpoll(xpfds, 2, -1)) < 0) || rv != 1 || (xpfds[0].revents > 0)) { if (errno != 0) err("%p: %S: qcmd: xpoll (setting up stderr): %m\n", ahost); else err("%p: %S: qcmd: xpoll: protocol failure in circuit setup\n", ahost); (void) close(s2); goto bad; } s3 = accept(s2, (struct sockaddr *) &from, &len); (void) close(s2); if (s3 < 0) { err("%p: %S: qcmd: accept: %m\n", ahost); lport = 0; goto bad; } *fd2p = s3; from.sin_port = ntohs((u_short) from.sin_port); if (from.sin_family != AF_INET || from.sin_port >= IPPORT_RESERVED || from.sin_port < IPPORT_RESERVED / 2) { err("%p: %S: socket: protocol failure in circuit setup\n", ahost); goto bad2; } } (void) write(s, locuser, strlen(locuser) + 1); (void) write(s, remuser, strlen(remuser) + 1); (void) write(s, cmd, strlen(cmd) + 1); if (_qcmd_send_extra_args(s, nodeid) < 0) goto bad2; rv = read(s, &c, 1); if (rv < 0) { if (errno == EINTR) err("%p: %S: read: protocol failure: %s\n", ahost, "timed out"); else err("%p: %S: read: protocol failure: %m\n", ahost); goto bad2; } else if (rv != 1) { err("%p: %S: read: protocol failure: %s\n", ahost, "invalid response"); goto bad2; } if (c != 0) { /* retrieve error string from remote server */ char tmpbuf[LINEBUFSIZE]; char *p = tmpbuf; while (read(s, &c, 1) == 1) { *p++ = c; if (c == '\n') break; } if (c != '\n') *p++ = '\n'; *p++ = '\0'; err("%S: %s", ahost, tmpbuf); goto bad2; } pthread_sigmask(SIG_SETMASK, &oldset, NULL); return (s); bad2: if (lport) (void) close(*fd2p); bad: (void) close(s); pthread_sigmask(SIG_SETMASK, &oldset, NULL); return (-1); }
void connect_wait(void *pri_work) { drone_t *d=NULL; xpoll_t spdf[8]; unsigned int spdf_off=0; int pret=0, getret=0; uint8_t msg_type=0, status=0; size_t msg_len=0; uint8_t *ptr=NULL; time_t s_time=0, e_time=0; VRB(1, "waiting for connections to finish"); if (s->dlh == NULL || s->dlh->head == NULL) { PANIC("waiting for connections with no drones?"); } for (s_time=time(NULL);;) { int livesocks=0; for (d=s->dlh->head, spdf_off=0; d != NULL; d=d->next, spdf_off++) { if (d->s) livesocks++; spdf[spdf_off].fd=d->s; } DBG(M_CON, "polling %d sockets......", livesocks); if ((pret=xpoll(&spdf[0], s->dlh->size, 5000)) < 0) { ERR("poll drone fd's fail: %s", strerror(errno)); } time(&e_time); if ((e_time - s_time) > s->ss->recv_timeout) { break; } for (d=s->dlh->head, spdf_off=0; d != NULL; d=d->next, spdf_off++) { d->s_rw=0; if (d->status != DRONE_STATUS_DEAD && d->status != DRONE_STATUS_DONE) { d->s_rw=spdf[spdf_off].rw; } if (spdf[spdf_off].rw & XPOLL_READABLE) { DBG(M_CON, "socket type %s is readable", strdronetype(d->type)); } } for (d=s->dlh->head; d != NULL; d=d->next) { DBG(M_CON, "drone type %s drone status %s", strdronetype(d->type), strdronestatus(d->status)); if (d->type == DRONE_TYPE_LISTENER && (d->status == DRONE_STATUS_READY || d->status == DRONE_STATUS_WORKING)) { /* i just moved this here cause the line above was ugly */ if ((d->s_rw & XPOLL_READABLE) == XPOLL_READABLE) { if (recv_messages(d->s) < 1) { ERR("cant recv_messages from ready listener"); drone_updatestate(d, DRONE_STATUS_DEAD); continue; } while (1) { getret=get_message(d->s, &msg_type, &status, &ptr, &msg_len); if (getret < 1) { break; } if (msg_type == MSG_ERROR || status != MSG_STATUS_OK) { ERR("drone on fd %d is dead, closing socket and marking dead", d->s); drone_updatestate(d, DRONE_STATUS_DEAD); break; } else if (msg_type == MSG_OUTPUT) { deal_with_output(ptr, msg_len); } else { ERR("unhandled message from Listener drone message type `%s' with status %d", strmsgtype(msg_type), status); } } } } else if (d->type == DRONE_TYPE_SENDER && d->status == DRONE_STATUS_READY) { union { uint8_t *pw_ptr; void *ptr; send_pri_workunit_t *p; } pw_u; while ((pw_u.ptr=fifo_pop(pri_work)) != NULL) { DBG(M_CON, "sending pri work to sender in wait connections"); if (send_message( d->s, MSG_WORKUNIT, MSG_STATUS_OK, pw_u.pw_ptr, sizeof(send_pri_workunit_t) + pw_u.p->doff ) < 0) { ERR("cant send priority workunit to sender on fd %d, marking dead", d->s); drone_updatestate(d, DRONE_STATUS_DEAD); fifo_push(pri_work, pw_u.ptr); continue; } xfree(pw_u.ptr); } } if (s->senders == 0 || s->listeners == 0) { PANIC(s->senders == 0 ? "no senders" : "no listeners"); } } } VRB(1, "connections timeout"); }
static void _setup_socket(char *serv) { struct addrinfo hints, *res, *r; int *fds, fd, fdlen, saved_errno, count, error, i, opt; char *what = NULL; xpollfd_t pfd; struct sockaddr_storage addr; socklen_t addr_size; short flags; /* get addresses to listen on for this port */ memset(&hints, 0, sizeof(hints)); hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_PASSIVE; if ((error = getaddrinfo(NULL, serv, &hints, &res))) { fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(error)); exit(1); } if (res == NULL) { fprintf(stderr, "getaddrinfo: no address to bind to\n"); exit(1); } /* allocate array of listen fd's */ fdlen = 0; for (r = res; r != NULL; r = r->ai_next) fdlen++; fds = (int *)xmalloc(sizeof(int) * fdlen); /* bind fds to addresses and listen */ count = 0; saved_errno = 0; for (r = res, i = 0; r != NULL; r = r->ai_next, i++) { fds[i] = -1; if ((fd = socket(r->ai_family, r->ai_socktype, 0)) < 0) { saved_errno = errno; what = "socket"; continue; } opt = 1; if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) { saved_errno = errno; what = "setsockopt"; close(fd); continue; } if (bind(fd, r->ai_addr, r->ai_addrlen) < 0) { saved_errno = errno; what = "bind"; close(fd); continue; } if (listen(fd, LISTEN_BACKLOG) < 0) { saved_errno = errno; what = "listen"; close(fd); continue; } fds[i] = fd; count++; } freeaddrinfo(res); if (count == 0) { fprintf(stderr, "%s: %s\n", what, strerror(saved_errno)); exit(1); } /* accept a connection on 'fd' */ pfd = xpollfd_create(); fd = -1; while (fd == -1) { xpollfd_zero(pfd); for (i = 0; i < fdlen; i++) { if (fds[i] != -1) xpollfd_set(pfd, fds[i], XPOLLIN); } if (xpoll(pfd, NULL) < 0) { fprintf(stderr, "poll: %s\n", strerror(errno)); exit(1); } for (i = 0; i < fdlen; i++) { if (fds[i] != -1) { flags = xpollfd_revents(pfd, fds[i]); if ((flags & (XPOLLERR|XPOLLHUP|XPOLLNVAL))) { fprintf(stderr, "poll: error on fd %d\n", fds[i]); exit(1); } if ((flags & XPOLLIN)) { addr_size = sizeof(addr); fd = accept(fds[i], (struct sockaddr *)&addr, &addr_size); if (fd < 0) { fprintf(stderr, "accept: %s\n", strerror(errno)); exit(1); } break; } } } } xpollfd_destroy(pfd); for (i = 0; i < fdlen; i++) { if (fds[i] != -1 && fds[i] != fd) close(fds[i]); } /* dup socket to stdio */ (void)close(0); if (dup2(fd, 0) < 0) { fprintf(stderr, "dup2(stdin): %s\n", strerror(errno)); exit(1); } (void)close(1); if (dup2(fd, 1) < 0) { fprintf(stderr, "dup2(stdout): %s\n", strerror(errno)); exit(1); } }
/* * The rcmd call itself. * ahost (IN) remote hostname * addr (IN) IP address * locuser (IN) local username * remuser (IN) remote username * cmd (IN) command to execute * rank (IN) MPI rank * fd2p (IN/OUT) if non-NULL, open stderr backchannel on this fd * s (RETURN) socket for stdout/sdin or -1 on failure */ static int k4cmd(char *ahost, char *addr, char *locuser, char *remuser, char *cmd, int rank, int *fd2p, void **arg) { KTEXT_ST ticket; /* kerberos IV context */ CREDENTIALS cred; Key_schedule schedule; MSG_DAT msg_data; struct sockaddr_in faddr; struct sockaddr_in laddr; int s, pid; sigset_t oldset, blockme; struct sockaddr_in sin, from; char c; int lport = IPPORT_RESERVED - 1; unsigned long krb_options = 0L; static pthread_mutex_t mylock = PTHREAD_MUTEX_INITIALIZER; int status; int rc, rv; struct xpollfd xpfds[2]; pid = getpid(); sigemptyset(&blockme); sigaddset(&blockme, SIGURG); pthread_sigmask(SIG_BLOCK, &blockme, &oldset); for (;;) { s = privsep_rresvport(&lport); if (s < 0) { if (errno == EAGAIN) err("%p: %S: socket: All ports in use\n", ahost); else err("%p: %S: k4cmd: socket: %m\n", ahost); pthread_sigmask(SIG_SETMASK, &oldset, NULL); return (-1); } fcntl(s, F_SETOWN, pid); sin.sin_family = AF_INET; memcpy((caddr_t) & sin.sin_addr, addr, IP_ADDR_LEN); sin.sin_port = htons(KCMD_PORT); rv = connect(s, (struct sockaddr *) &sin, sizeof(sin)); if (rv >= 0) break; (void) close(s); if (errno == EADDRINUSE) { lport--; continue; } if (errno == EINTR) err("%p: %S: connect timed out\n", ahost); else err("%p: %S: %m\n", ahost); pthread_sigmask(SIG_SETMASK, &oldset, NULL); return (-1); } if (fd2p == 0) { write(s, "", 1); lport = 0; } else { char num[8]; int s2, s3; socklen_t len = sizeof(from); s2 = privsep_rresvport(&lport); if (s2 < 0) { goto bad; } listen(s2, 1); (void) snprintf(num, sizeof(num), "%d", lport); if (write(s, num, strlen(num) + 1) != strlen(num) + 1) { err("%p: %S: write: setting up stderr: %m\n", ahost); (void) close(s2); goto bad; } errno = 0; xpfds[0].fd = s; xpfds[1].fd = s2; xpfds[0].events = xpfds[1].events = XPOLLREAD; if (((rv = xpoll(xpfds, 2, -1)) < 0) || rv != 1 || (xpfds[0].revents > 0)) { if (errno != 0) err("%p: %S: k4cmd: xpoll (setting up stderr): %m\n", ahost); else err("%p: %S: k4cmd: xpoll: protocol failure in circuit setup\n", ahost); (void) close(s2); goto bad; } s3 = accept(s2, (struct sockaddr *) &from, &len); (void) close(s2); if (s3 < 0) { err("%p: %S: accept: %m\n", ahost); lport = 0; goto bad; } *fd2p = s3; from.sin_port = ntohs((u_short) from.sin_port); } /* * Kerberos-authenticated service. Don't have to send locuser, since * its already in the ticket, and we'll extract it on the other side. */ /*krb_options |= KOPT_DONT_CANON; */ pthread_mutex_lock(&mylock); status = krb_sendauth(krb_options, s, &ticket, "rcmd", ahost, NULL, (unsigned long) pid, &msg_data, &cred, schedule, &laddr, &faddr, "KCMDV0.1"); if (status != KSUCCESS) { /* * this part involves some very intimate knowledge of a * particular sendauth implementation to pry out the old * bits. This only catches the case of total failure -- but * that's the one where we get useful data from the remote * end. If we even get an authenticator back, then the * problem gets diagnosed locally anyhow. */ extern KRB_INT32 __krb_sendauth_hidden_tkt_len; char *old_data = (char *) &__krb_sendauth_hidden_tkt_len; char tmpbuf[LINEBUFSIZE]; char *p = tmpbuf; if ((status == KFAILURE) && (*old_data == 1)) { strncpy(tmpbuf, old_data + 1, 3); tmpbuf[3] = '\0'; err("%p: %S: %s", ahost, tmpbuf); *old_data = (-1); } if ((status == KFAILURE) && (*old_data == (char) -1)) { while (read(s, &c, 1) == 1) { /*(void) write(2, &c, 1); */ *p++ = c; if (c == '\n') break; } *p++ = '\0'; err("%p: %S: %s", ahost, tmpbuf); status = -1; } switch (status) { case KDC_PR_UNKNOWN: err("%p: %S: not registered for kerberos\n", ahost); break; case NO_TKT_FIL: err("%p: %S: no tickets file found\n", ahost); break; default: err("%p: %S: k4cmd failed: %s\n", ahost, (status == -1) ? "k4cmd protocol failure" : krb_get_err_text(status)); } pthread_mutex_unlock(&mylock); goto bad2; } pthread_mutex_unlock(&mylock); (void) write(s, remuser, strlen(remuser) + 1); (void) write(s, cmd, strlen(cmd) + 1); if ((rc = read(s, &c, 1)) != 1) { if (rc == -1) { err("%p: %S: read: %m\n", ahost); } else { err("%p: %S: k4cmd: bad connection with remote host\n", ahost); } goto bad2; } if (c != 0) { /* retrieve error string from remote server */ char tmpbuf[LINEBUFSIZE]; char *p = tmpbuf; while (read(s, &c, 1) == 1) { *p++ = c; if (c == '\n') break; } if (c != '\n') *p++ = '\n'; *p++ = '\0'; err("%S: %s", ahost, tmpbuf); goto bad2; } pthread_sigmask(SIG_SETMASK, &oldset, NULL); return (s); bad2: if (lport) (void) close(*fd2p); bad: (void) close(s); pthread_sigmask(SIG_SETMASK, &oldset, NULL); return (-1); }
void recv_packet(void) { char errbuf[PCAP_ERRBUF_SIZE], *pfilter=NULL; struct bpf_program filter; bpf_u_int32 net, mask; int ac_s=0, ret=0, worktodo=1; uint8_t msg_type=0, status=0, *ptr=NULL; size_t msg_len=0; xpoll_t spdf[2]; union { recv_workunit_t *r; uint8_t *cr; uint32_t *magic; } wk_u; union { listener_info_t *l; uint8_t *ptr; } l_u; union { drone_version_t *v; uint8_t *ptr; } d_u; drone_version_t dv; struct pcap_stat pcs; r_queue=fifo_init(); close_output_modules(); close_report_modules(); close_payload_modules(); DBG(M_IPC, "creating server socket"); memset(s->ss, 0, sizeof(scan_settings_t)); memset(&dv, 0, sizeof(dv)); d_u.v=&dv; dv.magic=DRONE_MAGIC; dv.maj=DRONE_MAJ; dv.min=DRONE_MIN; recv_stats_t recv_stats; /* heh */ if ((ac_s=socktrans_bind(s->ipcuri)) < 0) { terminate("cant create listener socket"); } DBG(M_IPC, "waiting for main to connect"); parent_sync(); lc_s=socktrans_accept(ac_s, DEF_SOCK_TIMEOUT); if (lc_s < 0) { terminate("main didnt connect, exiting"); } DBG(M_IPC, "got connection"); if (get_singlemessage(lc_s, &msg_type, &status, &ptr, &msg_len) != 1) { terminate("unexpected sequence of messages from parent waiting for ident request, exiting"); } if (msg_type != MSG_IDENT || status != MSG_STATUS_OK) { ERR("got an unknown message type `%s' or bad status %d from parent, exiting", strmsgtype(msg_type), status); } if (send_message(lc_s, MSG_IDENTLISTENER, MSG_STATUS_OK, d_u.ptr, sizeof(drone_version_t)) < 0) { terminate("cant send back msgident to parent"); } if (get_singlemessage(lc_s, &msg_type, &status, &ptr, &msg_len) != 1) { terminate("cant read ident ack message from parent, exiting"); } if (msg_type != MSG_ACK || status != MSG_STATUS_OK) { ERR("got an unknown message type `%s' or bad status %d from parent, exiting", strmsgtype(msg_type), status); } DBG(M_IPC, "sending ready message to parent"); l_u.l=(listener_info_t *)xmalloc(sizeof(listener_info_t)); memcpy(&l_u.l->myaddr, &s->vi[0]->myaddr, sizeof(struct sockaddr_storage)); memcpy(&l_u.l->mymask, &s->vi[0]->mymask, sizeof(struct sockaddr_storage)); memcpy(l_u.l->hwaddr, s->vi[0]->hwaddr, THE_ONLY_SUPPORTED_HWADDR_LEN); l_u.l->mtu=s->vi[0]->mtu; assert(s->interface_str != NULL); if (pcap_lookupnet(s->interface_str, &net, &mask, errbuf) < 0) { ERR("pcap_lookupnet fails, ignoring: %s", errbuf); } if (s->pcap_readfile == NULL) { pdev=pcap_open_live(s->interface_str, /* XXX haha */ s->vi[0]->mtu + 64, (GET_PROMISC() ? 1 : 0), 0, errbuf); if (pdev == NULL) { ERR("pcap open live: %s", errbuf); DBG(M_IPC, "sending ready error message to parent"); if (send_message(lc_s, MSG_READY, MSG_STATUS_ERROR, NULL, 0) < 0) { terminate("cant send message ready error"); } terminate("informed parent, exiting"); } } else { pdev=pcap_open_offline(s->pcap_readfile, errbuf); if (pdev == NULL) { ERR("pcap open offline: %s", errbuf); DBG(M_IPC, "sending ready error message to parent"); if (send_message(lc_s, MSG_READY, MSG_STATUS_ERROR, NULL, 0) < 0) { terminate("cant send message ready error"); } terminate("informed parent, exiting"); } } ret=util_getheadersize(pdev, errbuf); if (ret < 0 || ret > 0xffff) { ERR("error getting link header size: %s", errbuf); DBG(M_IPC, "sending ready error message to parent"); if (send_message(lc_s, MSG_READY, MSG_STATUS_ERROR, NULL, 0) < 0) { terminate("cant send message ready error"); } terminate("informed parent, exiting"); } s->ss->header_len=(uint16_t)ret; if (s->pcap_dumpfile != NULL) { VRB(0, "opening `%s' for pcap log", s->pcap_dumpfile); pdump=pcap_dump_open(pdev, s->pcap_dumpfile); if (pdump == NULL) { ERR("cant log to pcap file `%s'", pcap_geterr(pdev)); DBG(M_IPC, "sending ready error message to parent"); if (send_message(lc_s, MSG_READY, MSG_STATUS_ERROR, NULL, 0) < 0) { terminate("cant send message ready error"); } terminate("informed parent, exiting"); } } else { DBG(M_CLD, "not logging to pcap file"); } if (util_preparepcap(pdev, errbuf) < 0) { ERR("cant setup pcap filedesc to immediate mode: %s", errbuf); DBG(M_IPC, "sending ready error message to parent"); if (send_message(lc_s, MSG_READY, MSG_STATUS_ERROR, NULL, 0) < 0) { terminate("cant send message ready error"); } terminate("informed parent, exiting"); } /* pcap_fd will be -1 for a pcap file */ pcap_fd=pcap_get_selectable_fd(pdev); if (pcap_fd < 0 && s->pcap_readfile == NULL) { ERR("cant get selectable fd from pcap device, exiting"); DBG(M_IPC, "sending ready error message to parent"); if (send_message(lc_s, MSG_READY, MSG_STATUS_ERROR, NULL, 0) < 0) { terminate("sant send message ready error"); } terminate("informed parent, exiting"); } #ifdef PCAP_D_IN if (pcap_setdirection(pdev, PCAP_D_IN) < 0) { ERR("cant set pcap direction to in, exiting"); DBG(M_IPC, "sending ready error message to parent"); if (send_message(lc_s, MSG_READY, MSG_STATUS_ERROR, NULL, 0) < 0) { terminate("sant send message ready error"); } terminate("informed parent, exiting"); } #endif DBG(M_CLD, "listener dropping privs"); if (drop_privs() < 0) { terminate("cant drop privs"); } if (send_message(lc_s, MSG_READY, MSG_STATUS_OK, l_u.ptr, sizeof(listener_info_t)) < 0) { terminate("cant send message ready"); } xfree(l_u.l); /* XXX */ s->ss->syn_key=0; do { if (get_singlemessage(lc_s, &msg_type, &status, &wk_u.cr, &msg_len) != 1) { terminate("unexpected sequence of messages from parent looking for a workunit"); } if (status != MSG_STATUS_OK) { terminate("bad message status %u", status); } if (msg_type == MSG_QUIT) { worktodo=0; break; } else if (msg_type == MSG_WORKUNIT) { ; } else { terminate("unexpected message, expecting workunit or quit message"); } if (msg_len < sizeof(uint32_t)) { terminate("bad message, too short [" STFMT "]", msg_len); } if (msg_len < sizeof(recv_workunit_t)) { terminate("short workunit"); } worktodo=1; DBG(M_WRK, "workunit `%s'", strworkunit(wk_u.cr, msg_len)); s->ss->recv_timeout=wk_u.r->recv_timeout; s->ss->ret_layers=wk_u.r->ret_layers; s->recv_opts=wk_u.r->recv_opts; s->ss->window_size=wk_u.r->window_size; s->ss->syn_key=wk_u.r->syn_key; if (wk_u.r->pcap_len) { if ((msg_len - sizeof(recv_workunit_t)) == wk_u.r->pcap_len) { extract_pcapfilter(wk_u.cr + sizeof(recv_workunit_t), wk_u.r->pcap_len); } else { terminate("pcap option length illegal"); } } switch (*wk_u.magic) { case UDP_RECV_MAGIC: s->ss->mode=MODE_UDPSCAN; break; case TCP_RECV_MAGIC: s->ss->mode=MODE_TCPSCAN; break; case ARP_RECV_MAGIC: s->ss->mode=MODE_ARPSCAN; break; default: terminate("unknown recv workunit type"); break; } DBG(M_IPC, "from ipc, got workunit: %s", strworkunit((const void *)wk_u.cr, msg_len)); if (s->ss->mode == MODE_ARPSCAN) { if (s->ss->header_len != 14) { DBG(M_IPC, "sending msg error"); if (send_message(lc_s, MSG_READY, MSG_STATUS_ERROR, NULL, 0) < 0) { terminate("cant send message ready"); } terminate("wrong linktype for arp scan"); } } if (s->ss->ret_layers > 0) { DBG(M_CLD, "setting up packet queue"); p_queue=fifo_init(); } pfilter=get_pcapfilterstr(); VRB(1, "using pcap filter: `%s'", pfilter); memset(&filter, 0, sizeof(filter)); if (pcap_compile(pdev, &filter, pfilter, 0, net) < 0) { ERR("error compiling filter: %s", pcap_geterr(pdev)); if (send_message(lc_s, MSG_READY, MSG_STATUS_ERROR, NULL, 0) < 0) { ERR("cant send message ready error"); } terminate("cant compile pcap filter"); } if (pcap_setfilter(pdev, &filter) < 0) { ERR("error setting compiled filter: %s", pcap_geterr(pdev)); if (send_message(lc_s, MSG_READY, MSG_STATUS_ERROR, NULL, 0) < 0) { ERR("cant send message ready error"); } terminate("cant set compiled pcap filter"); } pcap_freecode(&filter); if (s->ss->ret_layers > 0) { DBG(M_IPC, "returning whole packet via ipc"); } DBG(M_IPC, "sending ready message to parent"); if (pcap_setnonblock(pdev, 1, errbuf) < 0) { terminate("cant set pcap non-blocking mode"); } if (send_message(lc_s, MSG_READY, MSG_STATUS_OK, NULL, 0) < 0) { terminate("cant send message ready"); } while (1) { spdf[0].fd=lc_s; spdf[1].fd=pcap_fd; /* if pdev is a socket ( ! -1 ) */ if (xpoll(&spdf[0], 2, -1) < 0) { ERR("xpoll fails: %s", strerror(errno)); } if (spdf[1].rw & XPOLL_READABLE) { pcap_dispatch(pdev, 1, parse_packet, NULL); } /* no packets, better drain the queue */ drain_pqueue(); if (spdf[0].rw & XPOLL_READABLE) { if (get_singlemessage(lc_s, &msg_type, &status, &ptr, &msg_len) != 1) { ERR("unexpected sequence of messages from parent in main read loop, exiting"); worktodo=0; break; } if (msg_type == MSG_TERMINATE) { DBG(M_IPC, "parent wants me to stop listening, breaking"); break; } else if (msg_type == MSG_QUIT) { DBG(M_IPC, "Parent wants me to quit, breaking"); worktodo=0; break; } else { ERR("got strange message `%s' from parent, exiting", strmsgtype(msg_type)); worktodo=0; break; } } } memset(&recv_stats, 0, sizeof(recv_stats)); if (pcap_stats(pdev, &pcs) != -1) { recv_stats.packets_recv=pcs.ps_recv; recv_stats.packets_dropped=pcs.ps_drop; recv_stats.packets_dropped=pcs.ps_ifdrop; } if (send_message(lc_s, MSG_WORKDONE, MSG_STATUS_OK, (void *)&recv_stats, sizeof(recv_stats)) < 0) { terminate("cant send workdone message to parent, exiting"); } } while (worktodo); pcap_close(pdev); if (s->pcap_dumpfile) { pcap_dump_close(pdump); } DBG(M_CLD, "listener exiting"); shutdown(lc_s, SHUT_RDWR); close(lc_s); uexit(0); }
void run_scan(void) { uint8_t msg_type=0, status=0, *ptr=NULL; size_t wk_len=0, msg_len=0; xpoll_t spdf[4]; /* XXX dynamic */ union { uint8_t *cr; void *ptr; } w_k; drone_t *c=NULL; rfifo=fifo_init(); if (GET_DOCONNECT()) { pri_work=fifo_init(); state_tbl=TBLINIT(111); } if (s->ss->mode == MODE_TCPSCAN) s->ss->syn_key=arc4random(); for (c=s->dlh->head ; c != NULL ; c=c->next) { if (c->type == DRONE_TYPE_LISTENER && c->status == DRONE_STATUS_READY) { if ((w_k.ptr=get_lp_workunit(&wk_len)) != NULL) { if (s->verbose > 2) { if (s->verbose > 5) { MSG(M_DBG2, "Got listener workunit of size %d :]", wk_len); } MSG(M_DBG1, "sending workunit to listener"); } if (send_message(c->s, MSG_WORKUNIT, MSG_STATUS_OK, w_k.cr, wk_len) < 0) { MSG(M_ERR, "Cant Send Workunit to listener on fd %d", c->s); mark_dead(c, DRONE_STATUS_DEAD); } if (c->s == -1) PANIC("WOW!!!!"); if (get_singlemessage(c->s, &msg_type, &status, &ptr, &msg_len) != 1) { MSG(M_ERR, "Unexpected sequence of messages from listener on fd %d, marking dead", c->s); mark_dead(c, DRONE_STATUS_DEAD); } if (status != MSG_STATUS_OK) { MSG(M_ERR, "bad status `%d' from listener on fd %d, marking as dead", status, c->s); mark_dead(c, DRONE_STATUS_DEAD); } if (msg_type != MSG_READY) { MSG(M_ERR, "bad message `%s' from listener on fd %d, marking as dead", strmsgtype(msg_type), c->s); mark_dead(c, DRONE_STATUS_DEAD); } else if (s->verbose > 3) { MSG(M_DBG1, "Sent workunits to listener on fd %d", c->s); } } } } if (s->listeners == 0) { MSG(M_ERR, "Not enough listeners to run scan, bailing out"); return; } while (1) { int readorwrite=0, breakout=0, pret=0; uint32_t d_offset=0; c=s->dlh->head; assert(s->dlh->size <= sizeof(spdf)); /* XXX */ /* write loop */ for (c=s->dlh->head, d_offset=0 ; c != NULL ; c=c->next, d_offset++) { if (c->type == DRONE_TYPE_SENDER) { void *pw_ptr=NULL; if (GET_DOCONNECT()) { while ((pw_ptr=fifo_pop(pri_work)) != NULL) { if (send_message(c->s, MSG_WORKUNIT, MSG_STATUS_OK, pw_ptr, sizeof(send_pri_workunit_t)) < 0) { MSG(M_ERR, "Cant send priority workunit to sender on fd %d, marking dead", c->s); mark_dead(c, DRONE_STATUS_DEAD); } } } if (c->status == DRONE_STATUS_READY) { /* get to work! */ w_k.cr=NULL; if ((w_k.ptr=get_sp_workunit(&wk_len)) != NULL) { if (s->verbose > 2) { if (s->verbose > 5) { MSG(M_DBG2, "Got workunit of size %d :]", wk_len); } MSG(M_DBG1, "sending workunit to sender"); } if (send_message(c->s, MSG_WORKUNIT, MSG_STATUS_OK, w_k.cr, wk_len) < 0) { MSG(M_ERR, "Cant Send Workunit to sender on fd %d", c->s); mark_dead(c, DRONE_STATUS_DEAD); } else if (s->verbose > 3) { MSG(M_DBG1, "Sent workunits to senders"); } c->status=DRONE_STATUS_WORKING; readorwrite=1; } else { if (s->verbose > 3) MSG(M_DBG1, "Marking sender on fd %d as done, no more workunits to send", c->s); send_message(c->s, MSG_QUIT, MSG_STATUS_OK, ptr, 0); mark_dead(c, DRONE_STATUS_DONE); } } } spdf[d_offset].fd=c->s; } if (!(s->senders)) { breakout++; break; } if ((pret=xpoll(&spdf[0], s->dlh->size, -1)) < 0) { MSG(M_ERR, "Poll drone fd's fail: %s", strerror(errno)); } for (c=s->dlh->head, d_offset=0 ; c != NULL ; c=c->next, d_offset++) { c->s_rw=0; if (c->status != DRONE_STATUS_DEAD && c->status != DRONE_STATUS_DONE) { c->s_rw=spdf[d_offset].rw; } if (spdf[d_offset].rw & XPOLL_READABLE) { if (s->verbose > 4) MSG(M_DBG1, "Socket type %s is readable", (c->type == DRONE_TYPE_LISTENER) ? "Listener" : "Sender"); } } /* read loop */ for (c=s->dlh->head, d_offset=0 ; c != NULL ; c=c->next, d_offset++) { if (c->status != DRONE_STATUS_DEAD && c->status != DRONE_STATUS_DONE && c->s_rw & XPOLL_READABLE) { int getret=0; if (s->verbose > 5) MSG(M_DBG2, "Reading file descriptor %d type %s and %d senders left", c->s, (c->type == DRONE_TYPE_SENDER ? "Sender" : "Listener"), s->senders); if (recv_messages(c->s) < 0) { MSG(M_ERR, "Cant recieve messages from fd %d, marking as dead", c->s); mark_dead(c, DRONE_STATUS_DEAD); continue; } while (1) { if (c->status == DRONE_STATUS_DONE || c->status == DRONE_STATUS_DEAD) break; getret=get_message(c->s, &msg_type, &status, &ptr, &msg_len); if (getret < 1) break; if (msg_type == MSG_ERROR || status != MSG_STATUS_OK) { MSG(M_ERR, "Drone on fd %d is dead, closing socket and marking dead", c->s); mark_dead(c, DRONE_STATUS_DEAD); break; } else if (msg_type == MSG_WORKDONE && c->type == DRONE_TYPE_SENDER) { if (s->verbose > 5) MSG(M_DBG2, "Setting sender back to ready state after workdone message"); c->status=DRONE_STATUS_READY; } else if (msg_type == MSG_OUTPUT && c->type == DRONE_TYPE_LISTENER) { if (s->ss->mode == MODE_TCPSCAN || s->ss->mode == MODE_UDPSCAN) { if (msg_len < sizeof(ip_report_t)) { MSG(M_ERR, "Unknown report format from listener"); } else { handle_ipoutput(ptr); } } else if (s->ss->mode == MODE_ARPSCAN) { handle_arpoutput(ptr); } } else { MSG(M_ERR, "Unhandled message from `%s' drone message type `%s' with status %d", (c->type == DRONE_TYPE_SENDER ? "Sender" : "Listener"), strmsgtype(msg_type), status); } if (getret == 0) break; } /* multiple message read loop */ } /* readable fd */ } if (breakout) break; } if (s->verbose > 3) MSG(M_DBG1, "###### Waiting for listener packet timeout %d seconds", s->ss->recv_timeout); if (1) { unsigned int remain=s->ss->recv_timeout; while (1) { remain=sleep(remain); if (remain == 0) { break; } } } while (1) { uint32_t d_offset=0; int pret=0; for (c=s->dlh->head ; c != NULL ; c=c->next) { if (c->type != DRONE_TYPE_LISTENER) { if (s->verbose > 7) MSG(M_DBG2, "skipping drone type %d", c->type); continue; } if (c->status == DRONE_STATUS_DEAD) { if (s->verbose > 5) MSG(M_DBG2, "skipping dead drone type %d", c->type); continue; } if (c->status == DRONE_STATUS_READY && !(GET_LISTENDRONE())) { if (send_message(c->s, MSG_TERMINATE, MSG_STATUS_OK, NULL, 0) < 0) { MSG(M_ERR, "Can't tell listener to quit, this scan is useless"); mark_dead(c, DRONE_STATUS_DEAD); continue; } if (s->verbose > 6) MSG(M_DBG2, "Told listener on fd %d to go into reporting mode", c->s); c->status=DRONE_STATUS_WORKING; } } for (c=s->dlh->head, d_offset=0 ; c != NULL ; c=c->next, d_offset++) { spdf[d_offset].fd=c->s; } if (s->listeners && (pret=xpoll(&spdf[0], s->dlh->size, -1)) < 0) { MSG(M_ERR, "Poll drone fd's fail: %s", strerror(errno)); } for (c=s->dlh->head, d_offset=0 ; c != NULL ; c=c->next, d_offset++) { c->s_rw=0; if (c->status != DRONE_STATUS_DEAD) c->s_rw=spdf[d_offset].rw; if (spdf[d_offset].rw & XPOLL_READABLE) { if (s->verbose > 7) MSG(M_DBG1, "Socket type %s is readable", (c->type == DRONE_TYPE_LISTENER) ? "Listener" : "Sender"); } } for (c=s->dlh->head ; c != NULL ; c=c->next) { if (c->status != DRONE_STATUS_DEAD && c->status != DRONE_STATUS_DONE && c->s_rw & XPOLL_READABLE) { int getret=0; if (recv_messages(c->s) < 0) { MSG(M_ERR, "read fd %d fails, marking as dead", c->s); mark_dead(c, DRONE_STATUS_DEAD); continue; } while (1) { if (c->status == DRONE_STATUS_DONE || c->status == DRONE_STATUS_DEAD) break; getret=get_message(c->s, &msg_type, &status, &ptr, &msg_len); if (getret < 1) break; if (s->verbose > 5) MSG(M_DBG2, "Got message type `%s [%d]' from a Listener Drone with status %d and %p data", strmsgtype(msg_type), msg_type, status, ptr); if (msg_type == MSG_ERROR || status != MSG_STATUS_OK) { MSG(M_ERR, "Got bad message from listener on fd %d, marking as dead", c->s); mark_dead(c, DRONE_STATUS_DEAD); continue; } else if (msg_type == MSG_OUTPUT) { if (s->ss->mode == MODE_TCPSCAN || s->ss->mode == MODE_UDPSCAN) { if (msg_len < sizeof(ip_report_t)) { MSG(M_ERR, "Unknown report format from listener on fd %d", c->s); } else { handle_ipoutput(ptr); } } else if (s->ss->mode == MODE_ARPSCAN) { handle_arpoutput(ptr); } } else if (msg_type == MSG_QUIT) { mark_dead(c, DRONE_STATUS_DONE); } else { MSG(M_ERR, "Unknown message from listener %d on fd %d, marking as dead", msg_type, c->s); /* hrmm, welp i dont get this drone, lets stop talking to him */ mark_dead(c, DRONE_STATUS_DEAD); } if (getret == 0) break; } /* while messages are read */ } } /* for reading listeners */ if (s->listeners == 0) break; } if (s->ss->mode == MODE_UDPSCAN || s->ss->mode == MODE_TCPSCAN) { do_report(); } else if (s->ss->mode == MODE_ARPSCAN) { do_arpreport(); } }