コード例 #1
0
ファイル: nsec3.c プロジェクト: umq/validns
void* nsec3_validate(struct rr *rrv)
{
    RRCAST(nsec3);

    if (!first_nsec3) {
        first_nsec3 = rr;
    }
    if (latest_nsec3) {
        if (memcmp(latest_nsec3->next_hashed_owner.data, rr->this_hashed_name.data, 20) != 0) {
            char *expected_name = quickstrdup_temp(rr->rr.rr_set->named_rr->name);
            /* guaranteed to have same length, I think */
            encode_base32hex(expected_name, 32, latest_nsec3->next_hashed_owner.data, 20);
            if (rr == first_nsec3) {
                moan(latest_nsec3->rr.file_name, latest_nsec3->rr.line,
                     "broken NSEC3 chain, expected %s, but nothing found",
                     expected_name);
            } else {
                moan(latest_nsec3->rr.file_name, latest_nsec3->rr.line,
                     "broken NSEC3 chain, expected %s, but found %s",
                     expected_name,
                     rr->rr.rr_set->named_rr->name);
            }
            if (rr != first_nsec3)
                latest_nsec3->next_nsec3 = rr;
            latest_nsec3 = rr;
            return NULL;
        }
        if (rr != first_nsec3)
            latest_nsec3->next_nsec3 = rr;
    }
    latest_nsec3 = rr;
    return rr;
}
コード例 #2
0
ファイル: dnskey.c プロジェクト: CAPSLOCK2000/validns
static void *dnskey_validate(struct rr *rrv)
{
	RRCAST(dnskey);

	if (G.opt.policy_checks[POLICY_DNSKEY]) {
		if (algorithm_type(rr->algorithm) == ALG_RSA_FAMILY) {
			unsigned int e_bytes;
			unsigned char *pk;
			int l;

			pk = (unsigned char *)rr->pubkey.data;
			l = rr->pubkey.length;

			e_bytes = *pk++;
			l--;
			if (e_bytes == 0) {
				if (l < 2)
					return moan(rr->rr.file_name, rr->rr.line, "public key is too short");
				e_bytes = (*pk++)  << 8;
				e_bytes += *pk++;
				l -= 2;
			}
			if (l < e_bytes)
				return moan(rr->rr.file_name, rr->rr.line, "public key is too short");

			if (*pk == 0)
				return moan(rr->rr.file_name, rr->rr.line, "leading zero octets in public key exponent");
			pk += e_bytes;
			l -= e_bytes;
			if (l > 0 && *pk == 0)
				return moan(rr->rr.file_name, rr->rr.line, "leading zero octets in key modulus");
		}
	}
	return NULL;
}
コード例 #3
0
ファイル: soa.c プロジェクト: jelu/validns
static void *soa_validate(struct rr *rrv)
{
    RRCAST(soa);

    if (strchr(rr->mname, '/') != NULL)
        return moan(rr->rr.file_name, rr->rr.line, "MNAME contains '/'");
    if (strchr(rr->rname, '/') != NULL)
        return moan(rr->rr.file_name, rr->rr.line, "RNAME contains '/'");
    return NULL;
}
コード例 #4
0
ファイル: privs.c プロジェクト: dankamongmen/libdank
static int
drop_privs_r(const char *username,const char *groupname,size_t buflen){
	struct passwd pwent,*pdb = NULL;
	struct group grent,*grp = NULL;
	uid_t uid,olduid = getuid();
	gid_t gid,oldgid = getgid();
	char buf[buflen];

	if(groupname){
		if(getgrnam_r(groupname,&grent,buf,sizeof(buf),&grp) || !grp){
			moan("Couldn't look up group %s\n",groupname);
			return -1;
		}
		gid = grp->gr_gid;
		if(setgid(gid)){
			moan("Couldn't setgid to group %d\n",gid);
			return -1;
		}
		if(getgid() != gid){
			bitch("getgid() returned %d, not %d\n",getgid(),gid);
			return -1;
		}
		if(getegid() != gid){
			bitch("getegid() returned %d, not %d\n",getegid(),gid);
			return -1;
		}
		nag("Dropped permissions to group %s (GID %d) from GID %d\n",groupname,gid,oldgid);
	}
	if(username){
		if(getpwnam_r(username,&pwent,buf,sizeof(buf),&pdb) || !pdb){
			moan("Couldn't look up username %s\n",username);
			return -1;
		}
		uid = pdb->pw_uid;
		if(setuid(uid)){
			moan("Couldn't setuid to user %d\n",uid);
			return -1;
		}
		if(getuid() != uid){
			bitch("getuid() returned %d, not %d\n",getuid(),uid);
			return -1;
		}
		if(geteuid() != uid){
			bitch("geteuid() returned %d, not %d\n",geteuid(),uid);
			return -1;
		}
		nag("Dropped permissions to user %s (UID %d) from UID %d\n",username,uid,olduid);
	}
	return 0;
}
コード例 #5
0
ファイル: tlsa.c プロジェクト: jelu/validns
static void* tlsa_validate_set(struct rr_set *rr_set)
{
    struct rr *rr;
    struct named_rr *named_rr;
    char *s;
    int port = 0;
    int len;

    if (G.opt.policy_checks[POLICY_TLSA_HOST]) {
        rr = rr_set->tail;
        named_rr = rr_set->named_rr;

        /* _25._tcp.mail.example.com. */
        s = named_rr->name;
        if (*s != '_') {
not_a_prefixed_domain_name:
            return moan(rr->file_name, rr->line, "not a proper prefixed DNS domain name");
        }
        s++;
        while (isdigit(*s)) {
            port = port * 10  + *s - '0';
            s++;
        }
        if (port <= 0 || port > 65535)  goto not_a_prefixed_domain_name;
        if (*s++ != '.')    goto not_a_prefixed_domain_name;
        len = strlen(s);
        if (len < 6)    goto not_a_prefixed_domain_name;
        if (memcmp(s, "_tcp.", 5) != 0 &&
            memcmp(s, "_udp.", 5) != 0 &&
            memcmp(s, "_sctp.", 6) != 0)    goto not_a_prefixed_domain_name;
    }
    return NULL;
}
コード例 #6
0
ファイル: tlsa.c プロジェクト: jelu/validns
static void* smimea_validate_set(struct rr_set *rr_set)
{
    struct rr *rr;
    struct named_rr *named_rr;
    char *s;
    int hash_len = 0;
    int len;

    if (G.opt.policy_checks[POLICY_SMIMEA_HOST]) {
        rr = rr_set->tail;
        named_rr = rr_set->named_rr;

        /* c93f1e400f26708f98cb19d936620da35eec8f72e57f9eec01c1afd6._smimecert.example.com. */
        s = named_rr->name;
        while (isxdigit(*s)) {
            hash_len++;
            s++;
        }
        if (*s++ != '.' || hash_len != 56) {
not_a_proper_smimea_domainname:
            return moan(rr->file_name, rr->line, "not a proper domain name for an SMIMEA record");
        }
        len = strlen(s);
        if (len < 11)    goto not_a_proper_smimea_domainname;
        if (memcmp(s, "_smimecert.", 11) != 0)    goto not_a_proper_smimea_domainname;
    }
    return NULL;
}
コード例 #7
0
ファイル: test_icapsrv.c プロジェクト: dankamongmen/snare
static int
icapstartlines_bad(const char **slines){
	struct sockaddr_in sina;
	const char **cur;
	int ret = -1,sd;

	if(common_start()){
		goto done;
	}
	memset(&sina,0,sizeof(sina));
	sina.sin_port = htons(ICAP_DEFAULT_PORT);
	sina.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
	sina.sin_family = AF_INET;
	for(cur = slines ; *cur ; ++cur){
		if((sd = make_tcp_socket(PF_INET)) >= 0){
			if(Connect(sd,(struct sockaddr *)&sina,sizeof(sina)) == 0){
				printf(" Sending %s",*cur);
				if((strlen(*cur) == 0 || Writen(sd,*cur,strlen(*cur)) == 0)){
					int i,v = 0;
					char c;

					while((i = read(sd,&c,sizeof(c))) == sizeof(c)){
						if(!v){
							printf("  ");
							v = 1;
						}
						putc(c,stdout);
						if(c == '\n'){
							v = 0;
						}
					}
					if(i == 0){
						if(Close(sd)){
							goto done;
						}
						continue; // mmm, spaghetti
					}else{
						if(i < 0){
							moan("Couldn't read reply on %d\n",sd);
						}else{
							bitch("Got %d reading on %d\n",i,sd);
						}
					}
				}
			}
			Close(sd);
		}
		goto done;
	}
	ret = 0;

done:
	ret |= close_icap_servers();
	ret |= reap_poller_thread(snarepoller);
	ret |= destroy_poller(snarepoller);
	ret |= stop_config();
	ret |= stop_fileconf();
	return ret;
}
コード例 #8
0
ファイル: aaaa.c プロジェクト: jelu/validns
static void* aaaa_validate_set(struct rr_set *rr_set)
{
    if (rr_set->named_rr->flags & NAME_FLAG_CONTAINS_SLASH) {
        struct rr *rr = rr_set->tail;
        return moan(rr->file_name, rr->line, "host name contains '/'");
    }
    return NULL;
}
コード例 #9
0
ファイル: logdir.c プロジェクト: dankamongmen/libdank
static void
truncate_crash_log_locked(int exit_status){
	int fd = -1;
	logctx *lc;

	if((lc = get_thread_logctx()) == NULL){
		goto done;
	}
	if(!crash_log){
		goto done;
	}
	if(avail_crash_log){
		timenag("Closing unused crash log\n");
		avail_crash_log = NULL;
		if(fclose(crash_log) == EOF){
			moan("Couldn't close crash log at %p\n",crash_log);
		}
		Unlink(crash_fn);
		goto done;
	}
	if(lc->lfile != crash_log){
		bitch("Crash log %p claimed, cur = %p\n",crash_log,lc->lfile);
		goto done;
	}
	if((fd = fileno(crash_log)) < 0){
		moan("Couldn't extract fd from crash log %p\n",crash_log);
		goto done;
	}
	nag("Truncating crash log at fd %d\n",fd);
	timenag("Halting! Exit code: %d\n",exit_status);
	Ftruncate(fd,lc->lfile_offset);
	// closing crash_log here in fatal sig handler context -> lockup, thus:
	lc->lfile = NULL; // FIXME

done:
	if(fd < 0){
		timenag("Halting with exit code %d\n",exit_status);
	}
	crash_log = NULL;
	free_logctx(lc);
	free(logdir);
	logdir = NULL;
	return;
}
コード例 #10
0
ファイル: dnskey.c プロジェクト: CAPSLOCK2000/validns
int dnskey_build_pkey(struct rr_dnskey *rr)
{
	if (rr->pkey_built)
		return rr->pkey ? 1 : 0;

	rr->pkey_built = 1;

	if (algorithm_type(rr->algorithm) == ALG_RSA_FAMILY) {
		RSA *rsa;
		EVP_PKEY *pkey;
		unsigned int e_bytes;
		unsigned char *pk;
		int l;

		rsa = RSA_new();
		if (!rsa)
			goto done;

		pk = (unsigned char *)rr->pubkey.data;
		l = rr->pubkey.length;

		e_bytes = *pk++;
		l--;
		if (e_bytes == 0) {
			if (l < 2) /* public key is too short */
				goto done;
			e_bytes = (*pk++)  << 8;
			e_bytes += *pk++;
			l -= 2;
		}
		if (l < e_bytes) /* public key is too short */
			goto done;

		rsa->e = BN_bin2bn(pk, e_bytes, NULL);
		pk += e_bytes;
		l -= e_bytes;

		rsa->n = BN_bin2bn(pk, l, NULL);

		pkey = EVP_PKEY_new();
		if (!pkey)
			goto done;

		if (!EVP_PKEY_set1_RSA(pkey, rsa))
			goto done;

		rr->pkey = pkey;
	}
done:
	if (!rr->pkey) {
		moan(rr->rr.file_name, rr->rr.line, "error building pkey");
	}
	return rr->pkey ? 1 : 0;
}
コード例 #11
0
ファイル: mx.c プロジェクト: umq/validns
static void *mx_validate(struct rr *rrv)
{
	RRCAST(mx);

	if (G.opt.policy_checks[POLICY_MX_ALIAS]) {
		if (find_rr_set(T_CNAME, rr->exchange)) {
			return moan(rr->rr.file_name, rr->rr.line, "MX exchange is an alias");
		}
	}
	return NULL;
}
コード例 #12
0
ファイル: poller.c プロジェクト: dankamongmen/snare
// Parameters are taken from epoll_ctl (see epoll_ctl(2))
static int
enqueue_epoll_change(poller *p,int fd,int op,uint32_t events){
	struct epoll_event ev;

	memset(&ev,0,sizeof(ev));
	ev.events = events;
	ev.data.fd = fd;
	if(epoll_ctl(p->epfd,op,fd,&ev)){
		moan("Couldn't register %d with epoll\n",fd);
		return -1;
	}
	return 0;
}
コード例 #13
0
ファイル: dnskey.c プロジェクト: CAPSLOCK2000/validns
void
dnskey_ksk_policy_check(void)
{
	struct rr_dnskey *rr = all_dns_keys;
	int ksk_found = 0;

	while (rr) {
		if (rr->key_type == KEY_TYPE_KSK)
			ksk_found = 1;
		rr = rr->next_key;
	}
	if (!ksk_found)
		moan(all_dns_keys->rr.file_name, all_dns_keys->rr.line, "No KSK found");
}
コード例 #14
0
ファイル: signals.c プロジェクト: dankamongmen/libdank
static void
signalfd_demultiplexer(int fd,void *cbstate){
	struct signalfd_siginfo si;
	evhandler *e = cbstate;
	ssize_t r;

	do{
		if((r = read(fd,&si,sizeof(si))) == sizeof(si)){
			handle_evsource_read(e->sigarray,si.ssi_signo);
		}else if(r >= 0){
			bitch("Got short read (%zd) off signalfd %d\n",r,fd);
			// FIXME stat!
		}
	}while(r >= 0 && errno != EINTR);
	if(errno != EAGAIN){
		moan("Error reading from signalfd %d\n",fd);
	}
}
コード例 #15
0
ファイル: writeq.c プロジェクト: dankamongmen/snare
static writeq_res
txstr(int sd,pending_msg *pm){
	ssize_t ret;

	do{ // precondition: amount to send > 0
	// loop around possible EINTRs
		if((ret = write(sd,pm->content.u.string + pm->off,pm->tosend)) < 0){
			if(errno == EAGAIN || errno == EWOULDBLOCK){
				return WRITEQ_RES_NBLOCK;
			}else if(errno != EINTR){ // loop on eintr
				moan("Error writing %zud on %d\n",pm->tosend,sd);
				return WRITEQ_RES_SYSERR;
			}
		}else{
			pm->off += ret;
			pm->tosend -= ret;
		}
	}while(pm->tosend);
	return WRITEQ_RES_SUCCESS;
}
コード例 #16
0
ファイル: writeq.c プロジェクト: dankamongmen/snare
// Suitable for edge-triggered event handling without signal-driven control
// flows (we arbitrarily loop on EINTR). sendfile(2) is used for file-backed
// maps, write(2) otherwise, but fallback from sendfile(2) errors to write(2)
// is not implemented.
static int
txfile(int sd,pending_msg *pm){
	ssize_t ret;

	do{ // precondition: amount to send > 0
		off_t oldoff = pm->off;

		/*if(pm->content.okey->fd >= 0){
			nag("%zu SENDFILE from %d to %d\n",pm->tosend,pm->content.okey->fd,sd);
			ret = sendfile_compat(sd,pm->content.okey->fd,&pm->off,pm->tosend);
		}else{*/
			//nag("%zu WRITE from %p to %d\n",pm->tosend,pm->content.okey->buf,sd);
			ret = write(sd,oqueue_const_ptrto(pm->content.okey,pm->off),
						pm->tosend);
			if(ret >= 0){
				pm->off += ret;
			}
		// }
		pm->tosend -= pm->off - oldoff;
		if(ret < 0){
			if(errno == EAGAIN || errno == EWOULDBLOCK){
				return WRITEQ_RES_NBLOCK;
			}else if(errno != EINTR){ // loop on EINTR
				moan("Error during %jd@%zu tx on %d\n",(intmax_t)pm->off,pm->tosend,sd);
				return WRITEQ_RES_SYSERR;
			}
		}
	}while(pm->tosend);
	if(pm->content.okey->cbarg){
		if(icap_state_verdictp(get_const_pfd_state(pm->content.okey->cbarg))){
			if(window_icap_encapsulate(pm->content.okey,pm->off)){
				return WRITEQ_RES_SYSERR;
			}
		}
	}
	return WRITEQ_RES_SUCCESS;
}
コード例 #17
0
ファイル: pollinbuf.c プロジェクト: dankamongmen/snare
static blob_read_res
read_blob(int sd,crlf_reader *br,oqueue_key *okey,size_t *bufoff,uint32_t *remains){
	size_t tosend,toread;
	ssize_t readnow;

	if(*remains == 0){
		bitch("Asked to read 0 bytes\n");
		return BLOB_READ_SYSERR;
	}
	// nag("Want %ju at %p:%zu from %d\n",(uintmax_t)*remains,buf,*bufoff,sd);
	// See if we can handle the request with what was left over
	if(br->count){
		tosend = br->count;
		if(tosend > *remains){
			tosend = *remains;
		}
		if(okey){
			memcpy(oqueue_ptrto(okey,*bufoff),br->buf,tosend);
		}
		*bufoff += tosend;
		if( (br->count -= tosend) ){
			memmove(br->buf,br->buf + tosend,br->count);
		}
		if((*remains -= tosend) == 0){
			return (br->count || br->eof || br->readreq) ?
				BLOB_READ_MOREDATA : BLOB_READ_SUCCESS;
		}
	}
	// We need more data. If we previously saw eof, we can't get it...
	if(br->eof){
		return BLOB_READ_SYSERR;
	}
	br->readreq = 0;
	while(br->total < *remains){
		// loop on a possible EINTR
		while((readnow = read(sd,br->buf,br->total)) < 0){
			if(errno == EAGAIN || errno == EWOULDBLOCK){
				return BLOB_READ_NBLOCK;
			}else if(errno != EINTR){
				moan("Error reading %zu from %d\n",br->total,sd);
				return BLOB_READ_SYSERR;
			}
		}
		if(readnow == 0){
			bitch("Got EOF, still wanted %u\n",*remains);
			return BLOB_READ_SYSERR;
		}
		tosend = readnow;
		if(okey){
			memcpy(oqueue_ptrto(okey,*bufoff),br->buf,readnow);
		}
		*bufoff += readnow;
		*remains -= readnow;
	}
	toread = *remains;
	// loop on a possible EINTR
	while((readnow = read(sd,br->buf,toread)) < 0){
		if(errno == EAGAIN || errno == EWOULDBLOCK){
			return BLOB_READ_NBLOCK;
		}else if(errno != EINTR){
			moan("Error reading %zu from %d\n",toread,sd);
			return BLOB_READ_SYSERR;
		}
	}
	if(readnow == 0){
		bitch("Got EOF, still wanted %zu\n",toread);
		return BLOB_READ_SYSERR;
	}
	tosend = readnow;
	if(okey){
		memcpy(oqueue_ptrto(okey,*bufoff),br->buf,tosend);
	}
	*bufoff += tosend;
	if((*remains -= readnow) == 0){
		br->readreq = 1;
		return BLOB_READ_MOREDATA;
	}
	return BLOB_READ_NBLOCK;
}
コード例 #18
0
ファイル: dnskey.c プロジェクト: jelu/validns
int dnskey_build_pkey(struct rr_dnskey *rr)
{
    if (rr->pkey_built)
        return rr->pkey ? 1 : 0;

    rr->pkey_built = 1;

    if (algorithm_type(rr->algorithm) == ALG_RSA_FAMILY) {
        RSA *rsa;
        EVP_PKEY *pkey;
        unsigned int e_bytes;
        unsigned char *pk;
        int l;

        rsa = RSA_new();
        if (!rsa)
            goto done;

        pk = (unsigned char *)rr->pubkey.data;
        l = rr->pubkey.length;

        e_bytes = *pk++;
        l--;
        if (e_bytes == 0) {
            if (l < 2) /* public key is too short */
                goto done;
            e_bytes = (*pk++)  << 8;
            e_bytes += *pk++;
            l -= 2;
        }
        if (l < e_bytes) /* public key is too short */
            goto done;

        rsa->e = BN_bin2bn(pk, e_bytes, NULL);
        pk += e_bytes;
        l -= e_bytes;

        rsa->n = BN_bin2bn(pk, l, NULL);

        pkey = EVP_PKEY_new();
        if (!pkey)
            goto done;

        if (!EVP_PKEY_set1_RSA(pkey, rsa))
            goto done;

        rr->pkey = pkey;
    } else if (algorithm_type(rr->algorithm) == ALG_ECC_FAMILY) {
        EC_KEY *pubeckey;
        EVP_PKEY *pkey;
        unsigned char *pk;
        int l;
        BIGNUM *bn_x = NULL;
        BIGNUM *bn_y = NULL;

        if (rr->algorithm == ALG_ECDSAP256SHA256) {
            l = SHA256_DIGEST_LENGTH;
            pubeckey = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
        } else if (rr->algorithm == ALG_ECDSAP384SHA384) {
            l = SHA384_DIGEST_LENGTH;
            pubeckey = EC_KEY_new_by_curve_name(NID_secp384r1);
        } else {
            goto done;
        }

        if (!pubeckey)
            goto done;

        if (rr->pubkey.length != 2*l) {
            goto done;
        }

        pk = (unsigned char *)rr->pubkey.data;

        bn_x = BN_bin2bn(pk, l, NULL);
        bn_y = BN_bin2bn(&pk[l], l, NULL);

        if (1 != EC_KEY_set_public_key_affine_coordinates(pubeckey, bn_x, bn_y)) {
            goto done;
        }

        pkey = EVP_PKEY_new();
        if (!pkey)
            goto done;

        if (!EVP_PKEY_assign_EC_KEY(pkey, pubeckey))
            goto done;

        rr->pkey = pkey;
    }
done:
    if (!rr->pkey) {
        moan(rr->rr.file_name, rr->rr.line, "error building pkey");
    }
    return rr->pkey ? 1 : 0;
}
コード例 #19
0
ファイル: lineparser.c プロジェクト: dankamongmen/libdank
// Heap allocates and returns the next line, as delimited by '\n'. Caller must
// free() this result. Comments (lines beginning with optional whitespace and
// a '#' character) are stripped. Returns NULL on end of file or I/O error;
// check errno to differentiate. Errno of 0 indicates standard EOF (success).
char *line_parser_next(line_parser_ctx *ctx){
	unsigned examined = 0;

	while(ctx->count > examined || !ctx->eof){
		char *end;

		// if we've examined all that we've read, we need to read more!
		// if we can't read anymore, return what we have!
		if(ctx->count == examined){
			size_t tlen,uret;
			ssize_t ret;

			if(ctx->count == ctx->total - 1){
				char *tmp;

				if((tmp = Realloc("line parser buf",ctx->buf,ctx->total + BUFSIZ)) == NULL){
					return NULL;
				}
				ctx->total += BUFSIZ;
				ctx->buf = tmp;
			}else if(ctx->count > ctx->base){
				memmove(ctx->buf,ctx->buf + ctx->base,ctx->count);
			}else if(ctx->count){
				memcpy(ctx->buf,ctx->buf + ctx->base,ctx->count);
			}
			ctx->base = 0;
			tlen = ctx->total - ctx->count - 1;
			ret = read(ctx->fd,ctx->buf + ctx->count,tlen);
			if(ret < 0){
				ctx->eof = 1;
				moan("Couldn't read %zu from %d\n",tlen,ctx->fd);
				return NULL;
			}else if((uret = ret) < tlen){
				ctx->eof = 1;
				if(uret == 0){
					break;
				}
			}
			ctx->count += uret;
			ctx->buf[ctx->count] = '\0';
		}
		// we must have something (empty read broke out)
		if(examined == 0){
			int joinlines = 0,joinc = 0,joinb = 0;

			while(ctx->count){
				if(isspace(ctx->buf[ctx->base])){
					if(ctx->buf[ctx->base] == '\n'){
						if(joinlines){
							joinlines = 0;
							ctx->buf[joinb] = ' ';
							ctx->buf[ctx->base] = ' ';
						}
					}
					--ctx->count;
					++ctx->base;
				}else if(ctx->buf[ctx->base] == '\\'){
					if(joinlines){
						ctx->count = joinc;
						ctx->base = joinb;
						break;
					}else{
						joinlines = 1;
						joinc = ctx->count;
						joinb = ctx->base;
					}
				}else{
					if(joinlines){
						ctx->count = joinc;
						ctx->base = joinb;
					}
					break;
				}
			}
			if(ctx->count == 0){
				continue;
			}
		}
		
		// still must have something (whitespace broke out)
		while( (end = strchr(ctx->buf + ctx->base + examined,'\n')) ){
			// safe; if '\n' was first, previous loop got it
			char *tmp = end;
			
			do{
				--tmp;
				if(*tmp == '\\'){
					*tmp = ' ';
					*end = ' ';
					examined += end - (ctx->buf + ctx->base + examined);
					end = NULL;
					break;
				}else if(!isspace(*tmp)){
					break;
				}
			}while(tmp != ctx->buf + ctx->base);
			if(end){
				break;
			}
		}

		if(end){
			if(is_comment(ctx->buf,ctx->base)){
				ctx->count -= (end - (ctx->buf + ctx->base) + 1);
				ctx->base += (end - (ctx->buf + ctx->base) + 1);
				examined = 0;
			}else{
				char *tmp = ctx->buf + ctx->base;

				ctx->count -= (end - (ctx->buf + ctx->base) + 1);
				ctx->base += (end - (ctx->buf + ctx->base) + 1);
				*end = '\0';
				return tmp;
			}
		}else{
			examined = ctx->count;
		}
	}
	
	// we have no data left to read, and it has no \n's; return what we have
	if(ctx->count && !is_comment(ctx->buf,ctx->base)){
		ctx->count = 0;
		return ctx->buf + ctx->base;
	}
	errno = 0;
	return NULL;
}