Exemple #1
0
/* Create the pipe used to awaken and terminate the helper processes
 *	must be called with the counter mutex */
static u_char
ready_helpers(void *logp)
{
	int fds[2];

	if (helper.pipe_write >= 0
	    && helper.pipe_read >= 0)
		return 1;

	terminate_helpers();

	/* give the helper child processes an FD that will go dead
	 * if the parent dies or otherwise closes the other end */
	dcc_clean_stdio();
	if (0 > pipe(fds)) {
		thr_error_msg(logp, "DNSBL parent helper pipe(): %s",
			      ERROR_STR());
		terminate_helpers();
		return 0;
	}
	helper.pipe_read = fds[0];
	helper.pipe_write = fds[1];
	if (0 > fcntl(helper.pipe_write, F_SETFD, FD_CLOEXEC)) {
		thr_error_msg(logp, "DNSBL helper fcntl(FD_CLOEXEC): %s",
			      ERROR_STR());
		terminate_helpers();
		return 0;
	}

	return 1;
}
Exemple #2
0
/* open the helper socket used by helper processes to receive requests
 *	must be called with the counter mutex */
static u_char
open_helper_soc(DCC_CLNT_CTXT *ctxt, void *logp)
{
	socklen_t soc_len;
	static int rcvbuf = 32*1024;
	static u_char rcvbuf_set;
	char sustr[DCC_SU2STR_SIZE];
	DCC_EMSG emsg;

	rcvbuf_set = 0;

	/* We want to create a new socket with the same choice of
	 * IPv4 or IPv6 as the DCC client context's socket.  To do that,
	 * try to ensure that the context's socket is healthy. */
	dcc_ctxts_lock();
	if (!dcc_clnt_rdy(emsg, ctxt,
			  DCC_CLNT_FG_BAD_SRVR_OK
			  | DCC_CLNT_FG_NO_MEASURE_RTTS
			  | DCC_CLNT_FG_NO_FAIL)
	    || !dcc_info_unlock(emsg))
		thr_trace_msg(logp, "DNSBL helper: %s", emsg);
	dcc_ctxts_unlock();
	dcc_mk_loop_su(&helper.su, ctxt->soc[0].loc.sa.sa_family, 0);

	dcc_clean_stdio();
	if (!udp_create(emsg, &helper.soc, &helper.su, 0)) {
		thr_error_msg(logp, "DNSBL helper bind(%s): %s",
			      dcc_su2str(sustr, sizeof(sustr), &helper.su),
			      emsg);
		terminate_helpers();
		return 0;
	}
	soc_len = sizeof(helper.su);
	if (0 > getsockname(helper.soc, &helper.su.sa, &soc_len)) {
		thr_error_msg(logp, "DNSBL helper getsockname(%d, %s): %s",
			      helper.soc, dcc_su2str(sustr, sizeof(sustr),
						     &helper.su),
			      ERROR_STR());
		terminate_helpers();
		return 0;
	}
	for (;;) {
		if (!setsockopt(helper.soc, SOL_SOCKET, SO_RCVBUF,
				&rcvbuf, sizeof(rcvbuf)))
			break;
		if (rcvbuf_set || rcvbuf <= 4096) {
			thr_error_msg(logp,
				      "DNSBL setsockopt(%s,SO_RCVBUF=%d): %s",
				      dcc_su2str(sustr, sizeof(sustr),
						 &helper.su),
				      rcvbuf, ERROR_STR());
			break;
		}
		rcvbuf -= 4096;
	}
	rcvbuf_set = 1;
	return 1;
}
static void init_ge2d()
{
	ge2d_fd = open("/dev/ge2d", O_RDWR);
	if (ge2d_fd < 0)
	{
		ERROR_STR("Can't open /dev/ge2d\n");
	}


	memclient_fd = open("/dev/memclient", O_RDWR);
	if (memclient_fd < 0)
	{
		ERROR_STR("Can't open /dev/memclient\n");
	}
}
Exemple #4
0
/* this is needed only on systems without coherent mmap()/read()/write() */
static void
sync_white(WF *wf)
{
	if (wf->wtbl
	    && 0 > MSYNC(wf->wtbl, wf->wtbl_size, MS_SYNC))
		dcc_error_msg("msync(%s): %s", wf->ht_nm, ERROR_STR());
}
Exemple #5
0
static u_char
close_white_ht(DCC_EMSG emsg, WF *wf)
{
	u_char result = 1;

	if (wf->ht_fd < 0)
		return result;

	wf->closed = 1;

	unmap_white_ht(wf);

#ifdef DCC_WIN32
	/* unlock the file before closing it to keep Win95 happy */
	if (!dcc_unlock_fd(emsg, wf->ht_fd, DCC_LOCK_ALL_FILE,
			   "whitelist ", wf->ht_nm))
		result = 0;
#endif
	if (0 > close(wf->ht_fd)) {
		dcc_pemsg(EX_IOERR, emsg, "close(%s): %s",
			  wf->ht_nm, ERROR_STR());
		result = 0;
	}

	wf->ht_fd = -1;
	memset(&wf->ht_sb, 0, sizeof(wf->ht_sb));
#ifndef DCC_WIN32
	wf->ht_sb.st_dev = -1;
	wf->ht_sb.st_ino = -1;
#endif
	return result;
}
Exemple #6
0
int
read_db(DCC_EMSG emsg, void *buf, u_int buf_len,
	int fd, off_t pos, const char *file_nm)
{
	int i;

	if (-1 == lseek(fd, pos, SEEK_SET)) {
		dcc_pemsg(EX_IOERR, emsg, "lseek(%s, 0): %s",
			  DB_NM2PATH_ERR(file_nm), ERROR_STR());
		return -1;
	}

	i = read(fd, buf, buf_len);
	if (i >= 0)
		return i;

	dcc_pemsg(EX_IOERR, emsg, "read(%s): %s",
		  DB_NM2PATH_ERR(file_nm), ERROR_STR());
	return -1;
}
Exemple #7
0
static void
unmap_white_ht(WF *wf)
{
	if (!wf->wtbl)
		return;

	sync_white(wf);
#ifdef DCC_WIN32
	dcc_win32_unmap(&wf->ht_map, wf->wtbl, wf->ht_nm);
#else
	if (0 > munmap((void *)wf->wtbl, wf->wtbl_size))
		dcc_error_msg("munmap(%s,%d): %s",
			      wf->ht_nm, wf->wtbl_size, ERROR_STR());
#endif
	wf->wtbl = 0;
}
Exemple #8
0
int				/* 0=ok -1=failed */
unlink_white_ht(DCC_EMSG emsg,
		WF *wf,
		time_t now)		/* 0=our own new file */
{
	int result;

	/* mark it bad if it is a brand new hash table */
	if (!now && wf->wtbl && !(wf->wf_flags & WF_RO))
		wf->wtbl->hdr.ascii_mtime = 0;

#ifdef DCC_WIN32
	/* racy but you cannot unlink an open file on WIN32 */
	close_white_ht(0, wf);
#endif
	result = -1;
	if (!(wf->wf_flags & WF_RO)) {
		if (0 <= unlink(wf->ht_nm) || errno == ENOENT) {
			result = 0;
#ifndef DCC_WIN32
		} else if ((errno == EACCES || errno == EPERM)
			   && dcc_get_priv_home(wf->ht_nm)) {
			if (0 <= unlink(wf->ht_nm))
				result = 0;
			dcc_rel_priv();
#endif
		}
		if (result < 0)
			dcc_pemsg(EX_IOERR, emsg,"unlink(%s): %s",
				  wf->ht_nm, ERROR_STR());
	}

	/* If we failed to unlink the old hash table,
	 * remember in core that things are broken but do not touch the file
	 * in case it is a link to /etc/passwd or something else dangerous */
	if (result < 0 && now)
		wf->broken = now + WHITE_BROKEN_DELAY;
#ifndef DCC_WIN32
	close_white_ht(0, wf);
#endif

	return result;
}
Exemple #9
0
static u_char				/* 1=done, 0=failed */
map_white_ht(DCC_EMSG emsg, WF *wf, WHITE_INX entries)
{
	size_t size;
#ifndef DCC_WIN32
	void *p;
#endif

	unmap_white_ht(wf);

	if (entries > MAX_WHITE_ENTRIES) {
		dcc_pemsg(EX_IOERR, emsg, "%s should not contain %d entries",
			  wf->ht_nm, entries);
		return 0;
	}

	size = ENTRIES2SIZE(entries);
#ifdef DCC_WIN32
	if (!wf->wtbl) {
		wf->wtbl = dcc_win32_map(emsg, &wf->ht_map, wf->ht_nm, wf->ht_fd,
					 size);
		if (!wf->wtbl)
			return 0;
	}
#else
	p = mmap(0, size,
		 (wf->wf_flags & WF_RO)
		 ? PROT_READ : (PROT_READ|PROT_WRITE),
		 MAP_SHARED, wf->ht_fd, 0);
	if (p == MAP_FAILED) {
		dcc_pemsg(EX_IOERR, emsg, "mmap(%s,%d): %s",
			  wf->ht_nm, (int)size, ERROR_STR());
		return 0;
	}
	wf->wtbl = p;
#endif
	wf->wtbl_size = size;
	wf->wtbl_entries = entries;
	wf->wtbl_fgs = wf->wtbl->hdr.fgs;
	wf->wtbl_sws = wf->wtbl->hdr.sws;

	return 1;
}
Exemple #10
0
int
main(int argc, char **argv)
{
	const char *to = 0;
	u_char print_version = 0;
	u_char first;
	DCC_PATH tmp;
	WHITE_INX inx, inx2;
	const char *nm;
	DCC_SUM sum;
	struct stat ht_sb, ascii_sb, inc_sb;
	char tgts_buf[20];
	char range_buf[INET6_ADDRSTRLEN+1+INET6_ADDRSTRLEN+1];
	int col;
	u_char heading;
	DCC_TGTS tgts;
	WHITE_LISTING listing;
	DCC_CK_TYPES type;
	int i;

	dcc_syslog_init(0, argv[0], 0);
	dcc_clnt_debug = 99;

	wf_init(&cmn_wf, WF_WLIST | WF_EITHER);

	while ((i = getopt(argc, argv, "VPQqh:t:")) != EOF) {
		switch (i) {
		case 'V':
			dcc_version_print();
			print_version = 1;
			break;

		case 'P':		/* parse or rebuild hash table */
			cmn_wf.wf_flags &= ~WF_WLIST_RO;
			cmn_wf.wf_flags |= WF_WLIST_RW;
			break;

		case 'Q':		/* query; don't rebuild hash table */
			cmn_wf.wf_flags &= ~WF_WLIST_RW;
			cmn_wf.wf_flags |= WF_WLIST_RO;
			break;

		case 'q':
			quiet = 1;
			break;

		case 'h':
			homedir = optarg;
			break;

		case 't':
			to = optarg;
			break;

		default:
			usage();
		}
	}
	argc -= optind;
	argv += optind;
	if (argc == 0) {
		if (print_version)
			exit(EX_OK);
		usage();
	}

	if (!dcc_cdhome(0, homedir, 1) && homedir)
		dcc_cdhome(0, homedir = 0, 1);

	first = 1;
	while ((nm = *argv++) != 0) {
		if (first) {
			first = 0;
		} else {
			printf("\n\n--------------------------------\n");
		}

		if (cmn_wf.wf_flags & WF_WLIST_RO)
			cmn_wf.wf_flags |= WF_RO;
		else
			cmn_wf.wf_flags &= ~WF_RO;

		if (!dcc_new_white_nm(dcc_emsg, &cmn_wf, nm)) {
			dcc_error_msg("%s", dcc_emsg);
			exit(EX_DATAERR);
		}
		printf("%s\n", dcc_fnm2abs_msg(tmp, cmn_wf.ascii_nm));

		/* try to force re-parsing */
		if (cmn_wf.wf_flags & WF_WLIST_RW) {
			if (!new_ht_nm(dcc_emsg, &cmn_wf, 0)
			    || 0 > unlink_white_ht(dcc_emsg, &cmn_wf, time(0))) {
				dcc_error_msg("-P failed to force re-parsing;"
					      " %s",
					      dcc_emsg);
				exit(EX_DATAERR);
			}
			if (!dcc_new_white_nm(dcc_emsg, &cmn_wf, nm)) {
				dcc_error_msg("%s", dcc_emsg);
				exit(EX_DATAERR);
			}
		}

		switch (wf_rdy(dcc_emsg, &cmn_wf, &cmn_tmp_wf)) {
		case WHITE_OK:
			break;
		case WHITE_NOFILE:
			dcc_error_msg("does %s exist?", nm);
			exit(EX_DATAERR);
			break;
		case WHITE_CONTINUE:
			dcc_error_msg("%s", dcc_emsg);
			break;
		case WHITE_SILENT:
		case WHITE_COMPLAIN:
			dcc_error_msg("%s", dcc_emsg);
			exit(EX_DATAERR);
			break;
		}
		printf("%s\n", cmn_wf.wtbl->magic);

		if (to) {
			str2ck(&sum, 0, 0, to);
			printf("%s\n%8s %s\t",
			       to,
			       type2str_err(DCC_CK_ENV_TO, 0, 0, 0),
			       ck2str_err(DCC_CK_ENV_TO, &sum, 0));
			if (WHITE_OK != wf_sum(dcc_emsg, &cmn_wf,
					       DCC_CK_ENV_TO, &sum,
					       &tgts, &listing)) {
				dcc_error_msg("%s", dcc_emsg);
			}
			if (listing == WHITE_UNLISTED) {
				printf("unlisted\n");
			} else {
				printf("%s\n",
				       tgts2str(tgts_buf, sizeof(tgts_buf),
						tgts, 0));
			}
			continue;
		}

		printf("  %s whitelist  %d entries\n",
		       (cmn_wf.wtbl_fgs & WHITE_FG_PER_USER)
		       ? "per-user" : "global",
		       cmn_wf.wtbl->hdr.entries);

		if (0 > fstat(cmn_wf.ht_fd, &ht_sb)) {
			dcc_error_msg("stat(%s): %s",
				      cmn_wf.ht_nm, ERROR_STR());
			exit(EX_DATAERR);
		}

		if (0 > stat(cmn_wf.ascii_nm, &ascii_sb)) {
			dcc_error_msg("stat(%s): %s",
				      cmn_wf.ascii_nm, ERROR_STR());
		} else if (ht_sb.st_mtime < ascii_sb.st_mtime) {
			printf("    %s is older than %s\n",
			       cmn_wf.ht_nm, cmn_wf.ascii_nm);
		}

		if (cmn_wf.wtbl->hdr.ascii_mtime == 0) {
			printf("    %s broken\n", cmn_wf.ht_nm);
		} else if (cmn_wf.wtbl->hdr.ascii_mtime != ascii_sb.st_mtime) {
			printf("    %s has timestamp %s\n"
			       "\tfor %s which has mtime %s\n",
			       cmn_wf.ht_nm,
			       ts2buf(cmn_wf.wtbl->hdr.ascii_mtime),
			       cmn_wf.ascii_nm,
			       ts2buf(ascii_sb.st_mtime));
		}

		if (cmn_wf.wtbl->hdr.broken != 0)
			printf("    %s broken until %s\n",
			       cmn_wf.ht_nm,
			       ts2buf(cmn_wf.wtbl->hdr.broken));
		if (cmn_wf.wtbl->hdr.reparse != 0)
			printf("    re-parse %s for errors after %s\n",
			       cmn_wf.ascii_nm,
			       ts2buf(cmn_wf.wtbl->hdr.reparse));

		if (cmn_wf.wtbl->hdr.fgs & WHITE_FG_HOSTNAMES) {
			printf("    resolve host names after %s\n",
			       ts2buf(ht_sb.st_mtime + DCC_WHITECLNT_RESOLVE));
		} else if (!(cmn_wf.wtbl->hdr.fgs & WHITE_FG_PER_USER)) {
			printf("    contains no host names\n");
		}

		for (i = 0; i < DIM(cmn_wf.wtbl->hdr.white_incs); ++i) {
			if (cmn_wf.wtbl->hdr.white_incs[i].nm[0] == '\0')
				break;
			if (!i)
				printf("    includes\n");
			printf("      %s\n",
			       dcc_fnm2abs_msg(tmp,
					       cmn_wf.wtbl ->hdr.white_incs[
							i].nm));
			if (0 > stat(cmn_wf.wtbl->hdr.white_incs[i].nm,
				     &inc_sb)) {
				dcc_error_msg("stat(%s): %s",
					      cmn_wf.ascii_nm, ERROR_STR());
			} else if (ht_sb.st_mtime < inc_sb.st_mtime) {
				printf("    %s is older than %s"
				       " and needs rebuilding\n",
				       cmn_wf.ht_nm,
				       dcc_path2fnm(cmn_wf.wtbl->hdr.white_incs[i
							].nm));
			}
		}
		if (cmn_wf.wtbl_sws & WHITE_SW_DCC_ON)
			printf("    option DCC-on\n");
		if (cmn_wf.wtbl_sws & WHITE_SW_DCC_OFF)
			printf("    option DCC-off\n");

		if (cmn_wf.wtbl_sws & WHITE_SW_REP_ON)
			printf("    option dcc-rep-on\n");
		if (cmn_wf.wtbl_sws & WHITE_SW_REP_OFF)
			printf("    option dcc-rep-off\n");

		if (cmn_wf.wtbl_sws & WHITE_SW_GREY_ON)
			printf("    option greylist-on\n");
		if (cmn_wf.wtbl_sws & WHITE_SW_GREY_OFF)
			printf("    option greylist-off\n");

		if (cmn_wf.wtbl_sws & WHITE_SW_GREY_SPAM_ON)
			printf("    option greylist-ignore-spam-on\n");
		if (cmn_wf.wtbl_sws & WHITE_SW_GREY_SPAM_OFF)
			printf("    option greylist-ignore-spam-off\n");

		if (cmn_wf.wtbl_sws & WHITE_SW_LOG_ALL)
			printf("    option log-all\n");
		if (cmn_wf.wtbl_sws & WHITE_SW_LOG_NORMAL)
			printf("    option log-normal\n");

		if (cmn_wf.wtbl_sws & WHITE_SW_LOG_D)
			printf("    option log-subdirectory-day\n");
		if (cmn_wf.wtbl_sws & WHITE_SW_LOG_H)
			printf("    option log-subdirectory-hour\n");
		if (cmn_wf.wtbl_sws & WHITE_SW_LOG_M)
			printf("    option log-subdirectory-minute\n");


		if (cmn_wf.wtbl_sws & WHITE_SW_GREY_LOG_ON)
			printf("    option greylist-log-on\n");
		if (cmn_wf.wtbl_sws & WHITE_SW_GREY_LOG_OFF)
			printf("    option greylist-log-off\n");

		if (cmn_wf.wtbl_sws & WHITE_SW_MTA_FIRST)
			printf("    option MTA-first\n");
		if (cmn_wf.wtbl_sws & WHITE_SW_MTA_LAST)
			printf("    option MTA-last\n");

		for (i = 0; i < NUM_DNSBL_GROUPS; ++i) {
			if (cmn_wf.wtbl_sws & WHITE_SW_DNSBL_ON(i))
				printf("    option DNSBL%d-on\n", i+1);
			if (cmn_wf.wtbl_sws & WHITE_SW_DNSBL_OFF(i))
				printf("    option DNSBL%d-off\n", i+1);
		}

		if (cmn_wf.wtbl_sws & WHITE_SW_DISCARD_OK)
			printf("    option forced-discard-ok\n");
		if (cmn_wf.wtbl_sws & WHITE_SW_DISCARD_NO)
			printf("    option no-forced-discard\n");

		if (cmn_wf.wtbl_sws & WHITE_SW_TRAP_NOT)
			printf("    option "DCC_XHDR_TRAP_NOT"\n");
		if (cmn_wf.wtbl_sws & WHITE_SW_TRAP_DIS)
			printf("    option "DCC_XHDR_TRAP_DIS"\n");
		if (cmn_wf.wtbl_sws & WHITE_SW_TRAP_REJ)
			printf("    option "DCC_XHDR_TRAP_REJ"\n");

		for (type = DCC_CK_TYPE_FIRST;
		     type <= DCC_CK_TYPE_LAST;
		     ++type) {
			tgts = cmn_wf.wtbl->hdr.tholds_rej.t[type];
			if (tgts == THOLD_UNSET)
				continue;
			printf("    option threshold %s,%s\n",
			       type2str_err(type, 0, 0, 0),
			       thold2str(tgts_buf, sizeof(tgts_buf),
					 type, tgts));
		}

		printf("\n    file checksum %s\n",
		       ck2str_err(0, &cmn_wf.wtbl->hdr.ck_sum, 0));

		if (quiet)
			continue;

		/* display ranges of IP addresses */
		heading = 0;
		for (i = 0; i < cmn_wf.wtbl->hdr.ranges.len; ++i) {
			WHITE_IP_RANGE *r;

			if (!heading) {
				heading = 1;
				fputs("\n IP address blocks\n", stdout);
			}
			r = &cmn_wf.wtbl->hdr.ranges.rs[i];
			printf("%6s  %s\n",
			       tgts2str(tgts_buf, sizeof(tgts_buf), r->tgts, 0),
			       range2str(range_buf, sizeof(range_buf),
					 &r->range));
		}
		if (heading)
			putchar('\n');

		/* first the hash table */
		fputs("\n hash table\n", stdout);
		col = 0;
		for (inx = 0; inx < DIM(cmn_wf.wtbl->bins); ++inx) {
			if (!cmn_wf.wtbl->bins[inx]
			    && col == 0
			    && inx != 0) {
				inx2 = inx;
				while (inx2 < DIM(cmn_wf.wtbl->bins)
				       && !cmn_wf.wtbl->bins[inx2])
					++inx2;
				i = inx2 - inx;
				i -= i % 4;
				if (i != 0) {
					printf(" ...\n");
					inx += i;
				}
			}
			printf("%4d:", inx);
			if (cmn_wf.wtbl->bins[inx]) {
				printf("%-4d", cmn_wf.wtbl->bins[inx]);
			} else {
				printf("    ");
			}
			col = (col + 1) % 4;
			putchar(col == 0 ? '\n' : '\t');
		}

		/* then the entries */
		printf("\n\n%4s->%-4s  %12s  %6s\n",
		       "slot", "next", "type", "count");
		for (inx = 0; inx < cmn_wf.wtbl_entries; ++inx) {
			WHITE_ENTRY *e = &cmn_wf.wtbl->tbl[inx];
			if (e->type == DCC_CK_INVALID)
				continue;
			printf("%4d->%-4d  %12s  %6s  %s\n",
			       inx, e->fwd,
			       type2str_err(e->type, 0, 0, 0),
			       tgts2str(tgts_buf, sizeof(tgts_buf), e->tgts, 0),
			       ck2str_err(e->type, &e->sum, 0));
		}
	}
	exit(EX_OK);
}
Exemple #11
0
/* (re)load the client-ID and password database
 * -1=failed to find target,  0=sick file,  1=ok,  2=file unchanged */
int
load_ids(DCC_EMSG emsg,
	 DCC_CLNT_ID tgt_id,		/* DCC_ID_ANON or needed ID */
	 const ID_TBL **tgt_tbl,
	 u_char force,
	 u_char debug)
{
	DCC_PATH tmp;
	DCC_FNM_LNO_BUF fnm_buf;
	ID_TBL t, *tp, **tpp, **bin;
	FILE *f;
	int lno, passwords;
	u_char found_it;
	int result;
	char buf[sizeof(ID_TBL)*2+1];
	const char *bufp, *passbuf;
	char id_buf[30];
	struct stat sb;
	char *p, *p1;

	if (!id_tbl_blocks) {
		id_make_blocks(debug);
		id_tbl_expand_hash(debug);
	}

	if (tgt_tbl)
		*tgt_tbl = 0;

	if (!set_ids_path(emsg, 0))
		return -1;

	if (!force) {
		if (!dcc_ck_private(emsg, &sb, ids_path, -1)) {
			ids_mtime = 0;
			return -1;
		}

		if (ids_mtime == sb.st_mtime)
			return 2;
	}

	f = fopen(ids_path, "r");
	if (!f) {
		dcc_pemsg(EX_NOINPUT, emsg, "fopen(%s): %s",
			  dcc_fnm2abs_msg(tmp, ids_path), ERROR_STR());
		return -1;
	}

	/* the file contains passwords, so refuse to use it if anyone else
	 * can read it */
	if (!dcc_ck_private(emsg, &sb, ids_path, fileno(f))) {
		fclose(f);
		ids_mtime = 0;
		return -1;
	}

	/* empty the table by making all entries into placeholders */
	memset(&t, 0, sizeof(t));
	for (tpp = &id_tbl_hash[0]; tpp < &id_tbl_hash[id_tbl_bins]; ++tpp) {
		for (tp = *tpp; tp; tp = tp->hfwd) {
			t.hfwd = tp->hfwd;
			t.hbak = tp->hbak;
			t.id = tp->id;
			*tp = t;
		}
	}

	ids_mtime = sb.st_mtime;

	passwords = 0;
	lno = 0;
	result = 1;
	found_it = (tgt_id == DCC_ID_ANON);
	for (;;) {
		/* read and parse a line contain a client-ID and key(s) */
		bufp = fgets(buf, sizeof(buf), f);
		if (!bufp) {
			if (ferror(f) && result > 0) {
				dcc_pemsg(EX_IOERR, emsg, "fgets(%s): %s",
					  dcc_fnm2abs_msg(tmp, ids_path),
					  ERROR_STR());
				result = 0;
			}
			break;
		}
		++lno;

		/* Ignore blank lines and lines starting with '#'.
		 * Note that '#' flags a comment only at the start of
		 * the line to avoid dealing with the escaping hassles
		 * of allowing '#' in passwords. */
		bufp += strspn(bufp, DCC_WHITESPACE);
		if (*bufp == '\0' || *bufp == '#')
			continue;

		memset(&t, 0, sizeof(t));
		t.delay_inflate = DCC_ANON_INFLATE_OFF;

		/* Each substantive line has the form:
		 *	ID[,trace][,rpt-ok][,delay=ms[*inflate]] passwd1 passwd2
		 *	ID,delay=forever
		 *
		 * Placeholders have the from
		 *	ID
		 * or for servers on masters
		 *	ID,simple|ignore|rogue|commerical
		 *
		 *  Both passwords are always accepted.  They are intended
		 *  to be the previous and current or the current and
		 *  next to allow the password to be changed at both the
		 *  client and the server without loss of service. */

		passbuf = dcc_parse_word(emsg, id_buf, sizeof(id_buf),
					 bufp, "ID", ids_path, lno);
		if (!passbuf) {
			result = 0;	/* line too long */
			continue;
		}

		/* stop parsing the line if the server-ID is bad */
		p = strchr(id_buf, ',');
		if (p)
			*p++ = '\0';
		t.id = dcc_get_id(emsg, id_buf, ids_path, lno);
		if (t.id == DCC_ID_INVALID) {
			result = 0;
			continue;
		}
		if (t.id == DCC_ID_ANON) {
			dcc_pemsg(EX_DATAERR, emsg,
				  "invalid ID \"%s\"%s",
				  id_buf, dcc_fnm_lno(&fnm_buf, ids_path, lno));
			result = 0;
			continue;
		}

		/* parse the options */
		for (; p; p = p1) {
			p1 = strchr(p, ',');
			if (p1)
				*p1++ = '\0';

			if (!strcasecmp(p, "trace")) {
				if (!client_server(emsg, 0, t.id, p, lno)) {
					result = 0;
				} else {
					t.flags |= ID_FLG_TRACE;
				}
				continue;
			}

			if (!strcasecmp(p, "rpt-ok")
			    || !strcasecmp(p, "rpt_ok")) {
				if (!client_server(emsg, 0, t.id, p, lno)) {
					result = 0;
				} else {
					t.flags |= ID_FLG_RPT_OK;
				}
				continue;
			}

			if (!CLITCMP(p, "delay=")) {
				if (!client_server(emsg, 0, t.id, p, lno)) {
					result = 0;
				} else if (!parse_dccd_delay(emsg, &t.delay_us,
							&t.delay_inflate,
							p+LITZ("delay="),
							ids_path, lno)) {
					result = 0;
				} else {
					t.flags |= ID_FLG_DELAY_SET;
				}
				continue;
			}

			if (!strcasecmp(p, DCC_XHDR_ID_SIMPLE)) {
				if (!client_server(emsg, 1, t.id, p, lno)) {
					result = 0;
				} else {
				}
				continue;
			}
			if (!strcasecmp(p, DCC_XHDR_ID_IGNORE)) {
				if (!client_server(emsg, 1, t.id, p, lno)) {
					result = 0;
				}
				continue;
			}
			if (!strcasecmp(p, DCC_XHDR_ID_ROGUE)) {
				if (!client_server(emsg, 1, t.id, p, lno)) {
					result = 0;
				}
				continue;
			}
			if (!strcasecmp(p, DCC_XHDR_ID_REP_OK)) {
				if (!client_server(emsg, 1, t.id, p, lno)) {
					result = 0;
				}
				continue;
			}

			dcc_pemsg(EX_DATAERR, emsg,
				  "invalid option \"%s\"%s", p,
				  dcc_fnm_lno(&fnm_buf, ids_path, lno));
			result = 0;
		}

		if (*passbuf != '\0') {
			passbuf = parse_passwd(emsg, t.cur_passwd,
					       passbuf, "current password",
					       ids_path, lno);
			if (!passbuf) {
				result = 0; /* line too long */
				continue;
			}
			passbuf = parse_passwd(emsg, t.next_passwd,
					       passbuf, "next password",
					       ids_path, lno);
			if (!passbuf) {
				result = 0; /* line too long */
				continue;
			}
			if (*passbuf != '\0') {
				dcc_pemsg(EX_DATAERR, emsg,
					  "invalid next password%s",
					  dcc_fnm_lno(&fnm_buf, ids_path, lno));
				result = 0;
				continue;
			}
		}

		/* put the entry into the hash table if not already present */
		bin = &ID_HASH_ENTRY(t.id);
		tp = *bin;
		for (;;) {
			if (tp == 0) {
				tp = add_id_tbl(t.id, &bin, debug);
				break;
			} else if (tp->id == t.id) {
				break;
			}
			tp = tp->hfwd;
		}

		/* If the ID is already present, the file is bad unless
		 * the previous or current line is only a placeholder
		 * showing that the ID exists.
		 * Merge from the old entry to the new entry. */
		if (!(t.flags & ID_FLG_DELAY_SET)) {
			t.delay_us = tp->delay_us;
			t.delay_inflate = tp->delay_inflate;
			if (tp->flags & ID_FLG_DELAY_SET)
				t.flags |= ID_FLG_DELAY_SET;
		} else if ((tp->flags & ID_FLG_DELAY_SET)
			   && (t.delay_us != tp->delay_us
			       || t.delay_inflate != tp->delay_inflate)) {
			dcc_pemsg(EX_DATAERR, emsg,
				  "conflicting delays for ID %d%s",
				  t.id, dcc_fnm_lno(&fnm_buf, ids_path, lno));
			result = 0;
			continue;
		}
		if (t.cur_passwd[0] == '\0') {
			memcpy(t.cur_passwd, tp->cur_passwd,
			       sizeof(t.cur_passwd));
			memcpy(t.next_passwd, tp->next_passwd,
			       sizeof(t.next_passwd));
		} else {
			if (tp->cur_passwd[0] != '\0'
			    && (memcmp(t.cur_passwd, tp->cur_passwd,
				       sizeof(t.cur_passwd))
				|| memcmp(t.next_passwd, tp->next_passwd,
					  sizeof(t.next_passwd)))) {
				dcc_pemsg(EX_DATAERR, emsg,
					  "conflicting passwords for ID %d%s",
					  t.id,
					  dcc_fnm_lno(&fnm_buf, ids_path, lno));
				result = 0;
				continue;
			}
			++passwords;

			/* remember the target */
			if (t.id == tgt_id) {
				found_it = 1;
				if (tgt_tbl)
					*tgt_tbl = tp;
			}
		}
		t.flags |= (tp->flags & (ID_FLG_TRACE | ID_FLG_RPT_OK));
		t.hfwd = tp->hfwd;
		t.hbak = tp->hbak;
		*tp = t;
	}
	fclose(f);

	if (!passwords) {
		if (result > 0)
			dcc_pemsg(EX_DATAERR, emsg, "%s contains no passwords",
				  dcc_fnm2abs_msg(tmp, ids_path));
		result = -1;
	}
	if (!found_it) {
		if (result > 0)
			dcc_pemsg(EX_DATAERR, emsg,
				  "%s does not contain the password for ID %d",
				  dcc_fnm2abs_msg(tmp, ids_path), tgt_id);
		result = -1;
	}

	id_probes = 0;
	id_searches = 0;

	return result;
}
static void maliCopy(PixmapPtr pDstPixmap, int srcX, int srcY, int dstX, int dstY, int width, int height)
{
	PrivPixmap *srcPrivPixmap = (PrivPixmap *)exaGetPixmapDriverPrivate(SrcPixmap);
	PrivPixmap *dstPrivPixmap = (PrivPixmap *)exaGetPixmapDriverPrivate(pDstPixmap);


	IGNORE(pDstPixmap);
	IGNORE(srcX);
	IGNORE(srcY);
	IGNORE(dstX);
	IGNORE(dstY);
	IGNORE(width);
	IGNORE(height);


	//ERROR_STR("%s: pDstPixmap=%p srcX=%d srcY=%d dstX=%d dstY=%d width=%d height=%d\n",
	//	__FUNCTION__, pDstPixmap, srcX, srcY, dstX, dstY, width, height);


	ScreenPtr pScreen = SrcPixmap->drawable.pScreen;
	ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum];
	MaliPtr fPtr = MALIPTR(pScrn);

	struct fb_var_screeninfo var_info;
	int ret = ioctl(fPtr->fb_lcd_fd, FBIOGET_VSCREENINFO, &var_info);
	if (ret < 0)
	{
		ERROR_STR("FBIOGET_VSCREENINFO failed.\n");
	}


	unsigned char revX = 0;
	unsigned char revY = 0;

	if (dstY > srcY)
	{
		revY = 1;
	}

	if (dstX > srcX)
	{
		revX = 1;
	}

	//if (srcPrivPixmap->frameBufferNumber != 0)
	//{
	//	srcY += (var_info.yres * srcPrivPixmap->frameBufferNumber);
	//}

	//if (dstPrivPixmap->frameBufferNumber != 0)
	//{
	//	dstY += (var_info.yres * dstPrivPixmap->frameBufferNumber);
	//}

	ump_secure_id srcID = ump_secure_id_get(srcPrivPixmap->mem_info->handle);

	memclient_attach_dmabuf_param_t srcParam;
	srcParam.handle = srcID;
	ret = ioctl(memclient_fd, MEMCLIENT_ATTACH_UMP, &srcParam);
	if (ret < 0)
	{
		ERROR_STR("srcParam MEMCLIENT_ATTACH_UMP failed.\n");
	}


	ump_secure_id dstID = ump_secure_id_get(dstPrivPixmap->mem_info->handle);

	memclient_attach_dmabuf_param_t dstParam;
	dstParam.handle = dstID;
	ret = ioctl(memclient_fd, MEMCLIENT_ATTACH_UMP, &dstParam);
	if (ret < 0)
	{
		ERROR_STR("dstParam MEMCLIENT_ATTACH_UMP failed.\n");
	}



	// Configure GE2D
	struct config_para_ex_s configex;
	memset(&configex, 0x00, sizeof(configex));

	configex.src_para.mem_type = CANVAS_ALLOC;	//CANVAS_OSD0
	configex.src_para.left = 0;
	configex.src_para.top = 0;
	configex.src_para.width = var_info.xres;
	configex.src_para.height = var_info.yres;
	configex.src_para.x_rev = revX;
	configex.src_para.y_rev = revY;
	configex.src_planes[0].addr = srcParam.physical_address;
	configex.src_planes[0].w = configex.src_para.width;
	configex.src_planes[0].h = configex.src_para.height;
	configex.src_para.format = GE2D_FORMAT_S32_ARGB; // GE2D_FORMAT_S32_BGRA; // GE2D_FMT_S32_RGBA;


	configex.src2_para.mem_type = CANVAS_TYPE_INVALID;


	configex.dst_para.mem_type = CANVAS_ALLOC; // CANVAS_OSD0;
	configex.dst_para.left = 0;
	configex.dst_para.top = 0;
	configex.dst_para.width = configex.src_para.width;
	configex.dst_para.height = configex.src_para.height;
	configex.dst_para.x_rev = configex.src_para.x_rev;
	configex.dst_para.y_rev = configex.src_para.y_rev;
	configex.dst_planes[0].addr = dstParam.physical_address;
	configex.dst_planes[0].w = configex.dst_para.width;
	configex.dst_planes[0].h = configex.dst_para.height;
	configex.dst_para.format = GE2D_FORMAT_S32_ARGB;


	ret = ioctl(ge2d_fd, GE2D_CONFIG_EX, &configex);
	if (ret < 0)
	{
		ERROR_STR("GE2D_CONFIG_EX failed.\n");
	}

	// Perform the blit operation
	struct ge2d_para_s blitRect;
	memset(&blitRect, 0, sizeof(blitRect));

	blitRect.src1_rect.x = srcX;
	blitRect.src1_rect.y = srcY;
	blitRect.src1_rect.w = width;
	blitRect.src1_rect.h = height;
	blitRect.dst_rect.x = dstX;
	blitRect.dst_rect.y = dstY;

	if (revY) // && height > 1
	{
		blitRect.src1_rect.y -= 1;
	}

	ret = ioctl(ge2d_fd, GE2D_BLIT, &blitRect);
	if (ret < 0)
	{
		ERROR_STR("GE2D_BLIT failed.\n");
	}


	ret = ioctl(memclient_fd, MEMCLIENT_RELEASE_UMP, dstID);
	if (ret < 0)
	{
		ERROR_STR("dstID MEMCLIENT_RELEASE_UMP failed.\n");
	}

	ret = ioctl(memclient_fd, MEMCLIENT_RELEASE_UMP, srcID);
	if (ret < 0)
	{
		ERROR_STR("srcID MEMCLIENT_RELEASE_UMP failed.\n");
	}

}
Exemple #13
0
/* ask a helper process to do some filtering */
u_char					/* 1=got an answer */
ask_helper(DCC_CLNT_CTXT *ctxt, void *logp,
	   time_t avail_us,		/* spend at most this much time */
	   HELPER_REQ_HDR *req,		/* request sent to helper */
	   int req_len,
	   HELPER_RESP_HDR *resp,	/* put answer here */
	   int resp_len)
{
	DCC_EMSG emsg;
	DCC_SOCKU send_su;
	socklen_t su_len;
	DCC_SOCKU recv_su;
	char sustr[DCC_SU2STR_SIZE];
	char sustr2[DCC_SU2STR_SIZE];
	u_char counted;
	u_int gen;
	struct timeval now;
	time_t us;
	DCC_POLLFD pollfd;
	int i;

	emsg[0] = '\0';

	/* We will use the client context socket to talk to the helper,
	 * so ensure that it is open */
	if (!helper_soc_open(emsg, ctxt)) {
		thr_trace_msg(logp, "DNSBL reopen %s", emsg);
		return 0;
	}

	/* keep the lock until we have sent our request and wake-up call
	 * to ensure that some other thread does not shut down all of
	 * the helpers. */
	helper_lock();
	gettimeofday(&now, 0);

	/* If it has been a long time since we used a helper, then the last
	 * of them might be about to die of boredom.  Fix that race by
	 * restarting all of them.
	 * Most dying helpers should be reaped by the totals timer thread. */
	if (helper.idle_helpers > 0
	    && DCC_IS_TIME(now.tv_sec, helper.idle_restart,
			   HELPER_IDLE_RESTART)) {
		reap_helpers(1);
		if (helper.idle_helpers > 0)
			terminate_helpers();
		gettimeofday(&now, 0);
	}

	/* Restart all helpers if the current helper socket is the wrong
	 * family.  This should happen only when the DCC client library
	 * has chosen a new server */
	if (helper.soc != INVALID_SOCKET
	    && ctxt->soc[0].loc.sa.sa_family != AF_UNSPEC
	    && helper.su.sa.sa_family != ctxt->soc[0].loc.sa.sa_family) {
		terminate_helpers();
		gettimeofday(&now, 0);
	}

	helper.idle_restart = now.tv_sec + HELPER_IDLE_RESTART;

	if (helper.idle_helpers - helper.slow_helpers > 0) {
		/* avoid taking the last idle helper because there are
		 * usually fewer truly idle helpers than we think because
		 * we don't always wait for them to finish */
		if (helper.idle_helpers > 2
		    || helper.total_helpers >= helper.max_helpers
		    || !new_helper(ctxt, logp, req->id))
			--helper.idle_helpers;
		counted = 1;
	} else if (helper.total_helpers >= helper.max_helpers) {
		if (helper.debug > 0)
			thr_trace_msg(logp, "%s DNSBL %d idle, %d slow, and"
				      " %d total DNSBL helpers", req->id,
				      helper.idle_helpers, helper.slow_helpers,
				      helper.total_helpers);
		counted = 0;
	} else {
		if (!new_helper(ctxt, logp, req->id)) {
			helper_unlock();
			return 0;
		}
		counted = 1;
	}

	 /* The resolution of the BIND timeout limits is seconds, so even on
	  * systems where the timeout limits work, the helper might delay
	  * a second or two.  To keep the count of idle helpers as accurate
	  * as possible, always wait at least 1 second for an answer
	  * and 2 seconds for an answer to reach the parent. */
	req->avail_us = avail_us;
	avail_us += DCC_US;
	req->start = now;
	req->magic = HELPER_MAGIC_REQ;
	req->version = HELPER_VERSION;

	req->sn = ++helper.sn;
	gen = helper.gen;

	/* snapshot the address in case another thread restarts the helpers */
	send_su = helper.su;

	/* If the client context socket is connected but not to the helper
	 * socket,
	 * then either disconnect it or connect to the helper's socket */
	if (ctxt->soc[0].rem.sa.sa_family != AF_UNSPEC
	    && !DCC_SU_EQ(&ctxt->soc[0].rem, &send_su)
	    && !helper_soc_connect(emsg, ctxt, &send_su)) {
		thr_trace_msg(logp, "DNSBL soc_connect(): %s", emsg);
		help_finish(gen, 0, counted, 1);
		helper_unlock();
		return 0;
	}
	if (ctxt->soc[0].rem.sa.sa_family == AF_UNSPEC) {
		i = sendto(ctxt->soc[0].s, req, req_len, 0,
			   &send_su.sa, DCC_SU_LEN(&send_su));
	} else {
		i = send(ctxt->soc[0].s, req, req_len, 0);
	}
	if (i != req_len) {
		if (i < 0)
			thr_trace_msg(logp, "%s DNSBL sendto(%s): %s",
				      req->id, dcc_su2str(sustr, sizeof(sustr),
							&send_su),
				      ERROR_STR());
		else
			thr_trace_msg(logp, "%s DNSBL sendto(%s)=%d",
				      req->id, dcc_su2str(sustr, sizeof(sustr),
							&send_su),
				      i);
		help_finish(gen, 0, counted, 1);
		helper_unlock();
		return 0;
	}

	/* awaken a helper */
	i = write(helper.pipe_write, "x", 1);
	if (i != 1) {
		if (i < 0)
			thr_trace_msg(logp, "%s DNSBL write(pipe_write=%d): %s",
				      req->id, helper.pipe_write, ERROR_STR());
		else
			thr_trace_msg(logp, "%s DNSBL write(pipe_write)=%d",
				      req->id, i);
		help_finish(gen, 0, counted, 1);
		helper_unlock();
		return 0;
	}
	helper_unlock();

	for (;;) {
		us = avail_us - tv_diff2us(&now, &req->start);
		if (us < 0)
			us = 0;
		pollfd.fd = ctxt->soc[0].s;
		i = select_poll(emsg, &pollfd, 1, 1, us);
		if (i < 0) {
			thr_error_msg(logp, "%s DNSBL %s", req->id, emsg);
			help_finish(gen, 0, counted, 0);
			return 0;
		}
		gettimeofday(&now, 0);

		if (i == 0) {
			if (helper.debug)
				thr_trace_msg(logp,
					      "%s DNSBL no helper answer after"
					      " %1.f sec", req->id,
					      tv_diff2us(&now, &req->start)
					      / (DCC_US*1.0));
			helper_lock();
			if (helper.slow_helpers<=helper.total_helpers/MAX_SLOW)
				++helper.slow_helpers;
			help_finish(gen, 0, counted, 1);
			helper_unlock();
			return 0;
		}


		su_len = sizeof(recv_su);
		i = recvfrom(ctxt->soc[0].s, resp, resp_len,
			     0, &recv_su.sa, &su_len);
		/* because we are using UDP, we might get stray packets */
		if (i != resp_len) {
			if (i < 0) {
				thr_trace_msg(logp, "%s DNSBL recvfrom(): %s",
					      req->id, ERROR_STR());
				if (DCC_BLOCK_ERROR())
					continue;
				help_finish(gen, 0, counted, 0);
				return 0;
			}
			if (helper.debug > 1)
				thr_trace_msg(logp, "%s DNSBL recvfrom(%s)=%d",
					      req->id,
					      dcc_su2str(sustr, sizeof(sustr),
							&recv_su),
					      i);
			continue;
		}
		if (!DCC_SUnP_EQ(&send_su, &recv_su)) {
			if (helper.debug != 0)
				thr_trace_msg(logp, "%s DNSBL recvfrom(%s)"
					      " instead of %s",
					      req->id,
					      dcc_su2str(sustr, sizeof(sustr),
							&recv_su),
					      dcc_su2str(sustr2, sizeof(sustr2),
							&send_su));
			continue;
		}
		if (resp->magic != HELPER_MAGIC_RESP
		    || resp->version != HELPER_VERSION
		    || resp->sn != req->sn) {
			if (helper.debug >1 )
				thr_trace_msg(logp, "%s DNSBL recvfrom(%s)"
					      " magic=%#08x sn=%d",
					      req->id,
					      dcc_su2str(sustr, sizeof(sustr),
							&recv_su),
					      resp->magic, resp->sn);
			continue;
		}

		if (helper.debug > 4)
			thr_trace_msg(logp,"%s DNSBL answer from %s",
				      req->id,
				      dcc_su2str(sustr, sizeof(sustr),
						 &recv_su));

		help_finish(gen, 1, counted, 0);
		return 1;
	}
}
Exemple #14
0
/* Start a new helper process.
 *	The counter mutex must be locked */
static u_char
new_helper(DCC_CLNT_CTXT *ctxt, void *logp, const char *id)
{
	pid_t pid;
	char arg_buf[sizeof("set:")+sizeof(HELPER_PAT)+9+9+9];
	char trace_buf[200];
	char *bufp;
	DCC_SOCKET s;
	int pid_inx, buf_len, i, j;

	/* open the pipes and sockets if necessary */
	if (helper.soc == INVALID_SOCKET) {
		if (!ready_helpers(logp))
			return 0;
		if (!open_helper_soc(ctxt, logp))
			return 0;
	}

	reap_helpers(1);
	for (pid_inx = 0; ; ++pid_inx) {
		if (pid_inx >= helper.max_helpers)
			dcc_logbad(EX_SOFTWARE, "no free DNSBL pids[] entry");
		if (helper.pids[pid_inx] == 0)
			break;
	}

	fflush(stdout);
	fflush(stderr);
	pid = fork();
	if (pid < 0) {
		thr_error_msg(logp, "%s DNSBL helper fork(): %s",
			      id, ERROR_STR());
		return 0;
	}

	if (pid != 0) {
		/* this is the parent */
		helper.pids[pid_inx] = pid;
		++helper.total_helpers;
		return 1;
	}

	dcc_rel_priv();			/* no fun or games */
	dcc_clean_stdio();

	/* reset FD_CLOEXEC without affecting parent */
	s = helper.soc;
	if (s != INVALID_SOCKET) {
		s = dup(s);
		if (s == INVALID_SOCKET) {
			thr_error_msg(logp, "%s DNSBL soc dup(%d): %s",
				      id, helper.soc, ERROR_STR());
			return 0;
		}
	}

	snprintf(arg_buf, sizeof(arg_buf), "set:"HELPER_PAT,
		 s, helper.pipe_read, helper.total_helpers);
	helper_save_arg("-B", arg_buf);
	helper.argv[0] = dnsbl_progpath;
	buf_len = sizeof(trace_buf);
	bufp = trace_buf;
	for (i = 0; i < helper.argc && buf_len > 2; ++i) {
		j = snprintf(bufp, buf_len, "%s ", helper.argv[i]);
		buf_len -= j;
		bufp += j;
	}
	if (helper.debug >= 4)
		dcc_trace_msg("DNSBL helper exec %s", trace_buf);

	execv(helper.argv[0], (char * const *)helper.argv);
	/* This process should continue at helper_child() */

	dcc_logbad(EX_UNAVAILABLE, "execv(%s): %s",
		   trace_buf, ERROR_STR());
	return 0;
}
Exemple #15
-1
/* helper processes start here via fork()/exec() in the parent  */
void DCC_NORET
helper_child(DCC_SOCKET s, int fd, int total_helpers)
{
	sigset_t sigs;
	socklen_t soc_len;
	DNSBL_REQ req;
	int req_len;
	DNSBL_RESP resp;
	DCC_SOCKU req_su;
	socklen_t su_len;
	struct timeval now;
	u_char wake_buf;
	int secs, i;

	/* this process inherits via exec() by dccm or dccifd odd signal
	 * blocking from some pthreads implementations including FreeBSD 5.* */
	signal(SIGHUP, SIG_IGN);
	signal(SIGINT, SIG_IGN);
	signal(SIGTERM, SIG_IGN);
	sigemptyset(&sigs);
	sigaddset(&sigs, SIGALRM);
	sigprocmask(SIG_UNBLOCK, &sigs, 0);

	helper_init(0);
	if (have_helpers)
		dcc_logbad(EX_SOFTWARE, "no threads for DNSBL helpers");

	helper.total_helpers = total_helpers;

	helper.pipe_read = fd;
	helper.soc = s;
	soc_len = sizeof(helper.su);
	if (0 > getsockname(helper.soc, &helper.su.sa, &soc_len))
		dcc_logbad(EX_IOERR, "DNSBL helper getsockname(%d): %s",
			   helper.soc, ERROR_STR());

	if (helper.debug > 1)
		dcc_trace_msg("DNSBL helper process starting on %s",
			      dcc_su2str_err(&helper.su));

	for (;;) {
		/* Use read() and SIGALRM to watch for a wake-up byte
		 * from the parent, the parent ending and closing the pipe,
		 * or enough idle time to require our retirement.  This
		 * tactic awakens a single child for each wake-up call
		 * from the parent.  Using select() or poll() on the main
		 * socket awakens a thundering herd of children */
		secs = HELPER_IDLE_STOP_SECS+1;
		if (helper.total_helpers > 0)
			secs /= helper.total_helpers+1;
		if (secs < 5)
			secs = 5;
		signal(SIGALRM, helper_alarm);
#ifdef HAVE_SIGINTERRUPT
		siginterrupt(SIGALRM, 1);
#endif
		helper_alarm_hit = 0;
		alarm(secs);
		for (;;) {
			su_len = sizeof(req_su);
			req_len = recvfrom(helper.soc, &req, ISZ(req), 0,
					   &req_su.sa, &su_len);

			/* sleep until awakened if no work is ready */
			if (req_len <= 0) {
				if (req_len == 0)
					dcc_logbad(EX_IOERR,
						   "DNSBL helper recvfrom()=0");
				if (!DCC_BLOCK_ERROR())
					dcc_logbad(EX_IOERR,
						   "DNSBL helper recvfrom():"
						   " %s",
						   ERROR_STR());
				if (helper_alarm_hit)
					helper_exit("idle helper exit");

				i = read(helper.pipe_read, &wake_buf, 1);

				/* The other end of the pipe can be marked
				 * non-blocking by some pthreads
				 * implementations.  That makes read() on this
				 * end fail with EAGAIN.  When that happens,
				 * fall back on select() or poll().
				 * Even on such pthread implementations,
				 * it rarely happens. */
				if (i < 0 && DCC_BLOCK_ERROR()) {
					DCC_POLLFD pollfd;
					DCC_EMSG emsg;

					pollfd.fd = helper.pipe_read;
					i = select_poll(emsg, &pollfd, 1,
							0, -1);
					if (i < 0)
					    dcc_logbad(EX_IOERR,
						       "dnsbl HELPER %s", emsg);
				}

				/* loof for work after a wake-up call */
				if (i > 0)
					continue;

				if (helper_alarm_hit)
					continue;
				if (i == 0)
					helper_exit("shutdown");
				if (i < 0) {
					dcc_logbad(EX_OSERR,
						   "DNSBL read(terminate): %s",
						   ERROR_STR());
				}
			}
			if (req_len != helper.req_len) {
				if (helper.debug)
					dcc_trace_msg("DNSBL helper"
						      " recvfrom(parent %s)=%d"
						      " instead of %d",
						      dcc_su2str_err(&req_su),
						      req_len,
						      helper.req_len);
				continue;
			}

			/* we might get stray packets because we cannot
			 * connect to a single port */
			if (!DCC_SUnP_EQ(&helper.su, &req_su)) {
				if (helper.debug)
					dcc_trace_msg("DNSBL helper"
						    " request from"
						    " %s instead of %s",
						    dcc_su2str_err(&req_su),
						    dcc_su2str_err(&helper.su));
				continue;
			}

			if (req.hdr.magic != HELPER_MAGIC_REQ
			    || req.hdr.version != HELPER_VERSION) {
				if (helper.debug)
					dcc_trace_msg("DNSBL helper"
						      " recvfrom(parent %s)"
						      " magic=%#08x",
						      dcc_su2str_err(&req_su),
						      req.hdr.magic);
				continue;
			}
			break;
		}
		gettimeofday(&now, 0);
		alarm(0);

		/* do not bother working if it is already too late to answer,
		 * perhaps because a previous helper died */
		i = tv_diff2us(&now, &req.hdr.start);
		if (i >= req.hdr.avail_us) {
			if (helper.debug > 1)
				dcc_trace_msg("%s DNSBL helper"
					      " already too late to start;"
					      " used %.1f of %.1f seconds",
					      req.hdr.id, i / (DCC_US*1.0),
					      req.hdr.avail_us / (DCC_US*1.0));
			continue;
		}

		memset(&resp, 0, sizeof(resp));
		resp.hdr.magic = HELPER_MAGIC_RESP;
		resp.hdr.version = HELPER_VERSION;
		resp.hdr.sn = req.hdr.sn;

		/* do the work and send an answer if we have one */
		if (!dnsbl_work(&req, &resp))
			continue;

		/* do not answer if it is too late */
		gettimeofday(&now, 0);
		i = tv_diff2us(&now, &req.hdr.start);
		if (i > (req.hdr.avail_us + DCC_US/2)) {
			if (helper.debug > 1)
				dcc_trace_msg("%s DNSBL helper"
					      " too late to answer;"
					      " used %.1f of %.1f seconds",
					      req.hdr.id, i / (DCC_US*1.0),
					      req.hdr.avail_us / (DCC_US*1.0));
			continue;
		}

		i = sendto(helper.soc, &resp, sizeof(resp), 0,
			   &req_su.sa, DCC_SU_LEN(&req_su));
		if (i != sizeof(resp)) {
			if (i < 0)
				dcc_error_msg("%s helper sendto(%s): %s",
					      req.hdr.id,
					      dcc_su2str_err(&req_su),
					      ERROR_STR());
			else
				dcc_error_msg("%s helper sendto(%s)=%d",
					      req.hdr.id,
					      dcc_su2str_err(&req_su), i);
		}
	}
}