Exemple #1
0
static void
ind_ovs_upcall_request_pktin(uint32_t port_no, struct ind_ovs_port *port,
                             struct nlattr *packet, struct nlattr *key, uint8_t reason, uint64_t metadata)
{
    if (ind_ovs_benchmark_mode || aim_ratelimiter_limit(&port->pktin_limiter, monotonic_us()) == 0) {
        ind_ovs_bh_request_pktin(port_no, packet, key, reason, metadata);
    } else {
        if (aim_ratelimiter_limit(&port->upcall_log_limiter, monotonic_us()) == 0) {
            LOG_WARN("rate limiting packet-ins from port %s", port->ifname);
        }
    }
}
Exemple #2
0
/* Run command CMD and return statistics on it.
   Put the statistics in *RESP.  */
static void run_command(char *const *cmd, resource_t *resp)
{
	pid_t pid;			/* Pid of child.  */
	void (*interrupt_signal)(int);
	void (*quit_signal)(int);

	resp->elapsed_ms = monotonic_us() / 1000;
	pid = vfork();		/* Run CMD as child process.  */
	if (pid < 0)
		bb_perror_msg_and_die("fork");
	if (pid == 0) {	/* If child.  */
		/* Don't cast execvp arguments; that causes errors on some systems,
		   versus merely warnings if the cast is left off.  */
		BB_EXECVP(cmd[0], cmd);
		xfunc_error_retval = (errno == ENOENT ? 127 : 126);
		bb_error_msg_and_die("cannot run %s", cmd[0]);
	}

	/* Have signals kill the child but not self (if possible).  */
//TODO: just block all sigs? and reenable them in the very end in main?
	interrupt_signal = signal(SIGINT, SIG_IGN);
	quit_signal = signal(SIGQUIT, SIG_IGN);

	resuse_end(pid, resp);

	/* Re-enable signals.  */
	signal(SIGINT, interrupt_signal);
	signal(SIGQUIT, quit_signal);
}
Exemple #3
0
/* Run command CMD and return statistics on it.
   Put the statistics in *RESP.  */
static void run_command(char *const *cmd, resource_t * resp)
{
	pid_t pid;			/* Pid of child.  */
	__sighandler_t interrupt_signal, quit_signal;

	resp->elapsed_ms = monotonic_us() / 1000;
	pid = vfork();		/* Run CMD as child process.  */
	if (pid < 0)
		bb_error_msg_and_die("cannot fork");
	else if (pid == 0) {	/* If child.  */
		/* Don't cast execvp arguments; that causes errors on some systems,
		   versus merely warnings if the cast is left off.  */
		BB_EXECVP(cmd[0], cmd);
		bb_error_msg("cannot run %s", cmd[0]);
		_exit(errno == ENOENT ? 127 : 126);
	}

	/* Have signals kill the child but not self (if possible).  */
	interrupt_signal = signal(SIGINT, SIG_IGN);
	quit_signal = signal(SIGQUIT, SIG_IGN);

	if (resuse_end(pid, resp) == 0)
		bb_error_msg("error waiting for child process");

	/* Re-enable signals.  */
	signal(SIGINT, interrupt_signal);
	signal(SIGQUIT, quit_signal);
}
uint32_t FAST_FUNC
next_random(random_t *rnd)
{
	/* Galois LFSR parameter */
	/* Taps at 32 31 29 1: */
	enum { MASK = 0x8000000b };
	/* Another example - taps at 32 31 30 10: */
	/* MASK = 0x00400007 */

	uint32_t t;

	if (UNINITED_RANDOM_T(rnd)) {
		/* Can use monotonic_ns() for better randomness but for now
		 * it is not used anywhere else in busybox... so avoid bloat
		 */
		INIT_RANDOM_T(rnd, getpid(), monotonic_us());
	}

	/* LCG has period of 2^32 and alternating lowest bit */
	rnd->LCG = 1664525 * rnd->LCG + 1013904223;
	/* Galois LFSR has period of 2^32-1 = 3 * 5 * 17 * 257 * 65537 */
	t = (rnd->galois_LFSR << 1);
	if (rnd->galois_LFSR < 0) /* if we just shifted 1 out of msb... */
		t ^= MASK;
	rnd->galois_LFSR = t;
	/* Both are weak, combining them gives better randomness
	 * and ~2^64 period. & 0x7fff is probably bash compat
	 * for $RANDOM range. Combining with subtraction is
	 * just for fun. + and ^ would work equally well. */
	t = (t - rnd->LCG) & 0x7fff;

	return t;
}
Exemple #5
0
/* pid_t is short on BSDI, so don't try to promote it.  */
static void resuse_end(pid_t pid, resource_t *resp)
{
	pid_t caught;

	/* Ignore signals, but don't ignore the children.  When wait3
	   returns the child process, set the time the command finished. */
	while ((caught = wait3(&resp->waitstatus, 0, &resp->ru)) != pid) {
		if (caught == -1 && errno != EINTR) {
			bb_perror_msg("wait");
			return;
		}
	}
	resp->elapsed_ms = (monotonic_us() / 1000) - resp->elapsed_ms;
}
Exemple #6
0
/* pid_t is short on BSDI, so don't try to promote it.  */
static int resuse_end(pid_t pid, resource_t * resp)
{
	int status;
	pid_t caught;

	/* Ignore signals, but don't ignore the children.  When wait3
	   returns the child process, set the time the command finished. */
	while ((caught = wait3(&status, 0, &resp->ru)) != pid) {
		if (caught == -1)
			return 0;
	}
	resp->elapsed_ms = (monotonic_us() / 1000) - resp->elapsed_ms;
	resp->waitstatus = status;
	return 1;
}
Exemple #7
0
int FAST_FUNC crypt_make_salt(char *p, int cnt /*, int x */)
{
	/* was: x += ... */
	int x = getpid() + monotonic_us();
	do {
		/* x = (x*1664525 + 1013904223) % 2^32 generator is lame
		 * (low-order bit is not "random", etc...),
		 * but for our purposes it is good enough */
		x = x*1664525 + 1013904223;
		/* BTW, Park and Miller's "minimal standard generator" is
		 * x = x*16807 % ((2^31)-1)
		 * It has no problem with visibly alternating lowest bit
		 * but is also weak in cryptographic sense + needs div,
		 * which needs more code (and slower) on many CPUs */
		*p++ = i64c(x >> 16);
		*p++ = i64c(x >> 22);
	} while (--cnt);
	*p = '\0';
	return x;
}
static int
wait_for_reply(len_and_sockaddr *from_lsa, struct sockaddr *to, unsigned *timestamp_us, int *left_ms)
{
	struct pollfd pfd[1];
	int read_len = 0;

	pfd[0].fd = rcvsock;
	pfd[0].events = POLLIN;
	if (*left_ms >= 0 && safe_poll(pfd, 1, *left_ms) > 0) {
		unsigned t;

		read_len = recv_from_to(rcvsock,
				recv_pkt, sizeof(recv_pkt),
				/*flags:*/ MSG_DONTWAIT,
				&from_lsa->u.sa, to, from_lsa->len);
		t = monotonic_us();
		*left_ms -= (t - *timestamp_us) / 1000;
		*timestamp_us = t;
	}

	return read_len;
}
void FAST_FUNC generate_uuid(uint8_t *buf)
{
	/* http://www.ietf.org/rfc/rfc4122.txt
	 *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
	 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	 * |                          time_low                             |
	 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	 * |       time_mid                |         time_hi_and_version   |
	 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	 * |clk_seq_and_variant            |         node (0-1)            |
	 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	 * |                         node (2-5)                            |
	 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	 * IOW, uuid has this layout:
	 * uint32_t time_low (big endian)
	 * uint16_t time_mid (big endian)
	 * uint16_t time_hi_and_version (big endian)
	 *  version is a 4-bit field:
	 *   1 Time-based
	 *   2 DCE Security, with embedded POSIX UIDs
	 *   3 Name-based (MD5)
	 *   4 Randomly generated
	 *   5 Name-based (SHA-1)
	 * uint16_t clk_seq_and_variant (big endian)
	 *  variant is a 3-bit field:
	 *   0xx Reserved, NCS backward compatibility
	 *   10x The variant specified in rfc4122
	 *   110 Reserved, Microsoft backward compatibility
	 *   111 Reserved for future definition
	 * uint8_t node[6]
	 *
	 * For version 4, these bits are set/cleared:
	 * time_hi_and_version & 0x0fff | 0x4000
	 * clk_seq_and_variant & 0x3fff | 0x8000
	 */
	pid_t pid;
	int i;

	i = open("/dev/urandom", O_RDONLY);
	if (i >= 0) {
		read(i, buf, 16);
		close(i);
	}
	/* Paranoia. /dev/urandom may be missing.
	 * rand() is guaranteed to generate at least [0, 2^15) range,
	 * but lowest bits in some libc are not so "random".  */
	srand(monotonic_us());
	pid = getpid();
	while (1) {
		for (i = 0; i < 16; i++)
			buf[i] ^= rand() >> 5;
		if (pid == 0)
			break;
		srand(pid);
		pid = 0;
	}

	/* version = 4 */
	buf[4 + 2    ] = (buf[4 + 2    ] & 0x0f) | 0x40;
	/* variant = 10x */
	buf[4 + 2 + 2] = (buf[4 + 2 + 2] & 0x3f) | 0x80;
}
Exemple #10
0
int makemime_main(int argc UNUSED_PARAM, char **argv)
{
	llist_t *opt_headers = NULL, *l;
	const char *opt_output;
#define boundary opt_output

	enum {
		OPT_c = 1 << 0,         // Content-Type:
		OPT_e = 1 << 1,         // Content-Transfer-Encoding. Ignored. Assumed base64
		OPT_o = 1 << 2,         // output to
		OPT_C = 1 << 3,         // charset
		OPT_N = 1 << 4,         // COMPAT
		OPT_a = 1 << 5,         // additional headers
		OPT_m = 1 << 6,         // COMPAT
		OPT_j = 1 << 7,         // COMPAT
	};

	INIT_G();

	// parse options
	opt_complementary = "a::";
	opts = getopt32(argv,
		"c:e:o:C:N:a:m:j:",
		&G.content_type, NULL, &opt_output, &G.opt_charset, NULL, &opt_headers, NULL, NULL
	);
	//argc -= optind;
	argv += optind;

	// respect -o output
	if (opts & OPT_o)
		freopen(opt_output, "w", stdout);

	// no files given on command line? -> use stdin
	if (!*argv)
		*--argv = (char *)"-";

	// put additional headers
	for (l = opt_headers; l; l = l->link)
		puts(l->data);

	// make a random string -- it will delimit message parts
	srand(monotonic_us());
	boundary = xasprintf("%d-%d-%d", rand(), rand(), rand());

	// put multipart header
	printf(
		"Mime-Version: 1.0\n"
		"Content-Type: multipart/mixed; boundary=\"%s\"\n"
		, boundary
	);

	// put attachments
	while (*argv) {
		printf(
			"\n--%s\n"
			"Content-Type: %s; charset=%s\n"
			"Content-Disposition: inline; filename=\"%s\"\n"
			"Content-Transfer-Encoding: base64\n"
			, boundary
			, G.content_type
			, G.opt_charset
			, bb_get_last_path_component_strip(*argv)
		);
		encode_base64(*argv++, (const char *)stdin, "");
	}

	// put multipart footer
	printf("\n--%s--\n" "\n", boundary);

	return EXIT_SUCCESS;
#undef boundary
}
Exemple #11
0
static int parse(const char *boundary, char **argv)
{
	char *line, *s, *p;
	const char *type;
	int boundary_len = strlen(boundary);
	const char *delims = " ;\"\t\r\n";
	const char *uniq;
	int ntokens;
	const char *tokens[32]; // 32 is enough

	// prepare unique string pattern
	uniq = xasprintf("%%llu.%u.%s", (unsigned)getpid(), safe_gethostname());

//bb_info_msg("PARSE[%s]", terminator);

	while ((line = xmalloc_fgets_str(stdin, "\r\n\r\n")) != NULL) {

		// seek to start of MIME section
		// N.B. to avoid false positives let us seek to the _last_ occurance
		p = NULL;
		s = line;
		while ((s=strcasestr(s, "Content-Type:")) != NULL)
			p = s++;
		if (!p)
			goto next;
//bb_info_msg("L[%s]", p);

		// split to tokens
		// TODO: strip of comments which are of form: (comment-text)
		ntokens = 0;
		tokens[ntokens] = NULL;
		for (s = strtok(p, delims); s; s = strtok(NULL, delims)) {
			tokens[ntokens] = s;
			if (ntokens < ARRAY_SIZE(tokens) - 1)
				ntokens++;
//bb_info_msg("L[%d][%s]", ntokens, s);
		}
		tokens[ntokens] = NULL;
//bb_info_msg("N[%d]", ntokens);

		// analyse tokens
		type = find_token(tokens, "Content-Type:", "text/plain");
//bb_info_msg("T[%s]", type);
		if (0 == strncasecmp(type, "multipart/", 10)) {
			if (0 == strcasecmp(type+10, "mixed")) {
				parse(xfind_token(tokens, "boundary="), argv);
			} else
				bb_error_msg_and_die("no support of content type '%s'", type);
		} else {
			pid_t pid = pid;
			int rc;
			FILE *fp;
			// fetch charset
			const char *charset = find_token(tokens, "charset=", CONFIG_FEATURE_MIME_CHARSET);
			// fetch encoding
			const char *encoding = find_token(tokens, "Content-Transfer-Encoding:", "7bit");
			// compose target filename
			char *filename = (char *)find_token(tokens, "filename=", NULL);
			if (!filename)
				filename = xasprintf(uniq, monotonic_us());
			else
				filename = bb_get_last_path_component_strip(xstrdup(filename));

			// start external helper, if any
			if (opts & OPT_X) {
				int fd[2];
				xpipe(fd);
				pid = vfork();
				if (0 == pid) {
					// child reads from fd[0]
					xdup2(fd[0], STDIN_FILENO);
					close(fd[0]); close(fd[1]);
					xsetenv("CONTENT_TYPE", type);
					xsetenv("CHARSET", charset);
					xsetenv("ENCODING", encoding);
					xsetenv("FILENAME", filename);
					BB_EXECVP(*argv, argv);
					_exit(EXIT_FAILURE);
				}
				// parent dumps to fd[1]
				close(fd[0]);
				fp = fdopen(fd[1], "w");
				signal(SIGPIPE, SIG_IGN); // ignore EPIPE
			// or create a file for dump
			} else {
				char *fname = xasprintf("%s%s", *argv, filename);
				fp = xfopen_for_write(fname);
				free(fname);
			}

			// housekeeping
			free(filename);

			// dump to fp
			if (0 == strcasecmp(encoding, "base64")) {
				decode_base64(stdin, fp);
			} else if (0 != strcasecmp(encoding, "7bit")
				&& 0 != strcasecmp(encoding, "8bit")) {
				// quoted-printable, binary, user-defined are unsupported so far
				bb_error_msg_and_die("no support of encoding '%s'", encoding);
			} else {
				// N.B. we have written redundant \n. so truncate the file
				// The following weird 2-tacts reading technique is due to
				// we have to not write extra \n at the end of the file
				// In case of -x option we could truncate the resulting file as
				// fseek(fp, -1, SEEK_END);
				// if (ftruncate(fileno(fp), ftell(fp)))
				//	bb_perror_msg("ftruncate");
				// But in case of -X we have to be much more careful. There is
				// no means to truncate what we already have sent to the helper.
				p = xmalloc_fgets_str(stdin, "\r\n");
				while (p) {
					if ((s = xmalloc_fgets_str(stdin, "\r\n")) == NULL)
						break;
					if ('-' == s[0] && '-' == s[1]
						&& 0 == strncmp(s+2, boundary, boundary_len))
						break;
					fputs(p, fp);
					p = s;
				}

/*
				while ((s = xmalloc_fgetline_str(stdin, "\r\n")) != NULL) {
					if ('-' == s[0] && '-' == s[1]
						&& 0 == strncmp(s+2, boundary, boundary_len))
						break;
					fprintf(fp, "%s\n", s);
				}
				// N.B. we have written redundant \n. so truncate the file
				fseek(fp, -1, SEEK_END);
				if (ftruncate(fileno(fp), ftell(fp)))
					bb_perror_msg("ftruncate");
*/
			}
			fclose(fp);

			// finalize helper
			if (opts & OPT_X) {
				signal(SIGPIPE, SIG_DFL);
				// exit if helper exited >0
				rc = wait4pid(pid);
				if (rc)
					return rc+20;
			}

			// check multipart finalized
			if (s && '-' == s[2+boundary_len] && '-' == s[2+boundary_len+1]) {
				free(line);
				break;
			}
		}
 next:
 		free(line);
	}

//bb_info_msg("ENDPARSE[%s]", boundary);

	return EXIT_SUCCESS;
}
Exemple #12
0
int arpping(uint32_t test_ip, uint32_t from_ip, uint8_t *from_mac, const char *interface)
{
	int timeout_ms;
	struct pollfd pfd[1];
#define s (pfd[0].fd)           /* socket */
	int rv = 1;             /* "no reply received" yet */
	struct sockaddr addr;   /* for interface name */
	struct arpMsg arp;

	s = socket(PF_PACKET, SOCK_PACKET, htons(ETH_P_ARP));
	if (s == -1) {
		bb_perror_msg(bb_msg_can_not_create_raw_socket);
		return -1;
	}

	if (setsockopt_broadcast(s) == -1) {
		bb_perror_msg("cannot enable bcast on raw socket");
		goto ret;
	}

	/* send arp request */
	memset(&arp, 0, sizeof(arp));
	memset(arp.h_dest, 0xff, 6);                    /* MAC DA */
	memcpy(arp.h_source, from_mac, 6);              /* MAC SA */
	arp.h_proto = htons(ETH_P_ARP);                 /* protocol type (Ethernet) */
	arp.htype = htons(ARPHRD_ETHER);                /* hardware type */
	arp.ptype = htons(ETH_P_IP);                    /* protocol type (ARP message) */
	arp.hlen = 6;                                   /* hardware address length */
	arp.plen = 4;                                   /* protocol address length */
	arp.operation = htons(ARPOP_REQUEST);           /* ARP op code */
	memcpy(arp.sHaddr, from_mac, 6);                /* source hardware address */
	memcpy(arp.sInaddr, &from_ip, sizeof(from_ip)); /* source IP address */
	/* tHaddr is zero-fiiled */                     /* target hardware address */
	memcpy(arp.tInaddr, &test_ip, sizeof(test_ip)); /* target IP address */

	memset(&addr, 0, sizeof(addr));
	safe_strncpy(addr.sa_data, interface, sizeof(addr.sa_data));
	if (sendto(s, &arp, sizeof(arp), 0, &addr, sizeof(addr)) < 0) {
		// TODO: error message? caller didn't expect us to fail,
		// just returning 1 "no reply received" misleads it.
		goto ret;
	}

	/* wait for arp reply, and check it */
	timeout_ms = 2000;
	do {
		int r;
		unsigned prevTime = monotonic_us();

		pfd[0].events = POLLIN;
		r = safe_poll(pfd, 1, timeout_ms);
		if (r < 0)
			break;
		if (r) {
			r = read(s, &arp, sizeof(arp));
			if (r < 0)
				break;
			if (r >= ARP_MSG_SIZE
			 && arp.operation == htons(ARPOP_REPLY)
			 /* don't check it: Linux doesn't return proper tHaddr (fixed in 2.6.24?) */
			 /* && memcmp(arp.tHaddr, from_mac, 6) == 0 */
			 && *((uint32_t *) arp.sInaddr) == test_ip
			) {
				rv = 0;
				break;
			}
		}
		timeout_ms -= (monotonic_us() - prevTime) / 1000;
	} while (timeout_ms > 0);

 ret:
	close(s);
	DEBUG("%srp reply received for this address", rv ? "No a" : "A");
	return rv;
}
Exemple #13
0
int popmaildir_main(int argc UNUSED_PARAM, char **argv)
{
	char *buf;
	unsigned nmsg;
	char *hostname;
	pid_t pid;
	const char *retr;
#if ENABLE_FEATURE_POPMAILDIR_DELIVERY
	const char *delivery;
#endif
	unsigned opt_nlines = 0;

	enum {
		OPT_b = 1 << 0,		// -b binary mode. Ignored
		OPT_d = 1 << 1,		// -d,-dd,-ddd debug. Ignored
		OPT_m = 1 << 2,		// -m show ugsed memory. Ignored
		OPT_V = 1 << 3,		// -V version. Ignored
		OPT_c = 1 << 4,		// -c use tcpclient. Ignored
		OPT_a = 1 << 5,		// -a use APOP protocol
		OPT_s = 1 << 6,		// -s skip authorization
		OPT_T = 1 << 7,		// -T get messages with TOP instead with RETR
		OPT_k = 1 << 8,		// -k keep retrieved messages on the server
		OPT_t = 1 << 9,		// -t90 set timeout to 90 sec
		OPT_R = 1 << 10,	// -R20000 remove old messages on the server >= 20000 bytes (requires -k). Ignored
		OPT_Z = 1 << 11,	// -Z11-23 remove messages from 11 to 23 (dangerous). Ignored
		OPT_L = 1 << 12,	// -L50000 not retrieve new messages >= 50000 bytes. Ignored
		OPT_H = 1 << 13,	// -H30 type first 30 lines of a message; (-L12000 -H30). Ignored
		OPT_M = 1 << 14,	// -M\"program arg1 arg2 ...\"; deliver by program. Treated like -F
		OPT_F = 1 << 15,	// -F\"program arg1 arg2 ...\"; filter by program. Treated like -M
	};

	// init global variables
	INIT_G();

	// parse options
	opt_complementary = "-1:dd:t+:R+:L+:H+";
	opts = getopt32(argv,
		"bdmVcasTkt:" "R:Z:L:H:" IF_FEATURE_POPMAILDIR_DELIVERY("M:F:"),
		&timeout, NULL, NULL, NULL, &opt_nlines
		IF_FEATURE_POPMAILDIR_DELIVERY(, &delivery, &delivery) // we treat -M and -F the same
	);
	//argc -= optind;
	argv += optind;

	// get auth info
	if (!(opts & OPT_s))
		get_cred_or_die(STDIN_FILENO);

	// goto maildir
	xchdir(*argv++);

	// launch connect helper, if any
	if (*argv)
		launch_helper((const char **)argv);

	// get server greeting
	pop3_checkr(NULL, NULL, &buf);

	// authenticate (if no -s given)
	if (!(opts & OPT_s)) {
		// server supports APOP and we want it?
		if ('<' == buf[0] && (opts & OPT_a)) {
			union { // save a bit of stack
				md5_ctx_t ctx;
				char hex[16 * 2 + 1];
			} md5;
			uint32_t res[16 / 4];

			char *s = strchr(buf, '>');
			if (s)
				s[1] = '\0';
			// get md5 sum of "<stamp>password" string
			md5_begin(&md5.ctx);
			md5_hash(&md5.ctx, buf, strlen(buf));
			md5_hash(&md5.ctx, G.pass, strlen(G.pass));
			md5_end(&md5.ctx, res);
			*bin2hex(md5.hex, (char*)res, 16) = '\0';
			// APOP
			s = xasprintf("%s %s", G.user, md5.hex);
			pop3_check("APOP %s", s);
			free(s);
			free(buf);
		// server ignores APOP -> use simple text authentication
		} else {
			// USER
			pop3_check("USER %s", G.user);
			// PASS
			pop3_check("PASS %s", G.pass);
		}
	}

	// get mailbox statistics
	pop3_checkr("STAT", NULL, &buf);

	// prepare message filename suffix
	hostname = safe_gethostname();
	pid = getpid();

	// get messages counter
	// NOTE: we don't use xatou(buf) since buf is "nmsg nbytes"
	// we only need nmsg and atoi is just exactly what we need
	// if atoi fails to convert buf into number it returns 0
	// in this case the following loop simply will not be executed
	nmsg = atoi(buf);
	free(buf);

	// loop through messages
	retr = (opts & OPT_T) ? xasprintf("TOP %%u %u", opt_nlines) : "RETR %u";
	for (; nmsg; nmsg--) {

		char *filename;
		char *target;
		char *answer;
		FILE *fp;
#if ENABLE_FEATURE_POPMAILDIR_DELIVERY
		int rc;
#endif
		// generate unique filename
		filename  = xasprintf("tmp/%llu.%u.%s",
			monotonic_us(), (unsigned)pid, hostname);

		// retrieve message in ./tmp/ unless filter is specified
		pop3_check(retr, (const char *)(ptrdiff_t)nmsg);

#if ENABLE_FEATURE_POPMAILDIR_DELIVERY
		// delivery helper ordered? -> setup pipe
		if (opts & (OPT_F|OPT_M)) {
			// helper will have $FILENAME set to filename
			xsetenv("FILENAME", filename);
			fp = popen(delivery, "w");
			unsetenv("FILENAME");
			if (!fp) {
				bb_perror_msg("delivery helper");
				break;
			}
		} else
#endif
		// create and open file filename
		fp = xfopen_for_write(filename);

		// copy stdin to fp (either filename or delivery helper)
		while ((answer = xmalloc_fgets_str(stdin, "\r\n")) != NULL) {
			char *s = answer;
			if ('.' == answer[0]) {
				if ('.' == answer[1])
					s++;
				else if ('\r' == answer[1] && '\n' == answer[2] && '\0' == answer[3])
					break;
			}
			//*strchrnul(s, '\r') = '\n';
			fputs(s, fp);
			free(answer);
		}

#if ENABLE_FEATURE_POPMAILDIR_DELIVERY
		// analyse delivery status
		if (opts & (OPT_F|OPT_M)) {
			rc = pclose(fp);
			if (99 == rc) // 99 means bail out
				break;
//			if (rc) // !0 means skip to the next message
				goto skip;
//			// 0 means continue
		} else {
			// close filename
			fclose(fp);
		}
#endif

		// delete message from server
		if (!(opts & OPT_k))
			pop3_check("DELE %u", (const char*)(ptrdiff_t)nmsg);

		// atomically move message to ./new/
		target = xstrdup(filename);
		strncpy(target, "new", 3);
		// ... or just stop receiving on failure
		if (rename_or_warn(filename, target))
			break;
		free(target);

#if ENABLE_FEATURE_POPMAILDIR_DELIVERY
 skip:
#endif
		free(filename);
	}

	// Bye
	pop3_check("QUIT", NULL);

	if (ENABLE_FEATURE_CLEAN_UP) {
		free(G.user);
		free(G.pass);
	}

	return EXIT_SUCCESS;
}
/* Returns 1 if no reply received */
int FAST_FUNC arpping(uint32_t test_nip,
		const uint8_t *safe_mac,
		uint32_t from_ip,
		uint8_t *from_mac,
		const char *interface)
{
	int timeout_ms;
	struct pollfd pfd[1];
#define s (pfd[0].fd)           /* socket */
	int rv = 1;             /* "no reply received" yet */
	struct sockaddr addr;   /* for interface name */
	struct arpMsg arp;

	s = socket(PF_PACKET, SOCK_PACKET, htons(ETH_P_ARP));
	if (s == -1) {
		bb_perror_msg(bb_msg_can_not_create_raw_socket);
		return -1;
	}

	if (setsockopt_broadcast(s) == -1) {
		bb_perror_msg("can't enable bcast on raw socket");
		goto ret;
	}

	/* send arp request */
	memset(&arp, 0, sizeof(arp));
	memset(arp.h_dest, 0xff, 6);                    /* MAC DA */
	memcpy(arp.h_source, from_mac, 6);              /* MAC SA */
	arp.h_proto = htons(ETH_P_ARP);                 /* protocol type (Ethernet) */
	arp.htype = htons(ARPHRD_ETHER);                /* hardware type */
	arp.ptype = htons(ETH_P_IP);                    /* protocol type (ARP message) */
	arp.hlen = 6;                                   /* hardware address length */
	arp.plen = 4;                                   /* protocol address length */
	arp.operation = htons(ARPOP_REQUEST);           /* ARP op code */
	memcpy(arp.sHaddr, from_mac, 6);                /* source hardware address */
	memcpy(arp.sInaddr, &from_ip, sizeof(from_ip)); /* source IP address */
	/* tHaddr is zero-filled */                     /* target hardware address */
	memcpy(arp.tInaddr, &test_nip, sizeof(test_nip));/* target IP address */

	memset(&addr, 0, sizeof(addr));
	safe_strncpy(addr.sa_data, interface, sizeof(addr.sa_data));
	if (sendto(s, &arp, sizeof(arp), 0, &addr, sizeof(addr)) < 0) {
		// TODO: error message? caller didn't expect us to fail,
		// just returning 1 "no reply received" misleads it.
		goto ret;
	}

	/* wait for arp reply, and check it */
	timeout_ms = 2000;
	do {
		typedef uint32_t aliased_uint32_t FIX_ALIASING;
		int r;
		unsigned prevTime = monotonic_us()/1000;

		pfd[0].events = POLLIN;
		r = safe_poll(pfd, 1, timeout_ms);
		if (r < 0)
			break;
		if (r) {
			r = safe_read(s, &arp, sizeof(arp));
			if (r < 0)
				break;

			//log3("sHaddr %02x:%02x:%02x:%02x:%02x:%02x",
			//	arp.sHaddr[0], arp.sHaddr[1], arp.sHaddr[2],
			//	arp.sHaddr[3], arp.sHaddr[4], arp.sHaddr[5]);

			if (r >= ARP_MSG_SIZE
			 && arp.operation == htons(ARPOP_REPLY)
			 /* don't check it: Linux doesn't return proper tHaddr (fixed in 2.6.24?) */
			 /* && memcmp(arp.tHaddr, from_mac, 6) == 0 */
			 && *(aliased_uint32_t*)arp.sInaddr == test_nip
			) {
				/* if ARP source MAC matches safe_mac
				 * (which is client's MAC), then it's not a conflict
				 * (client simply already has this IP and replies to ARPs!)
				 */
				if (!safe_mac || memcmp(safe_mac, arp.sHaddr, 6) != 0)
					rv = 0;
				//else log2("sHaddr == safe_mac");
				break;
			}
		}
		timeout_ms -= (unsigned)monotonic_us()/1000 - prevTime;
	} while (timeout_ms > 0);

 ret:
	close(s);
	log1("%srp reply received for this address", rv ? "No a" : "A");
	return rv;
}