Пример #1
0
 // Check to see if there is space for new input in the buffer,
 // and if there are any characters ready to read from stdin.
 //  
 void
 IODeviceUart::poll_for_input ()
 { 
   // If buffer is full OR no data is ready to be read return immediately
   //
   if ((buf.ib_chars >= UART_INBUF_SIZE)
       || (poll_fd(0) < 0)) {
     return;
   }
   
   // Lock UART buffer before accessing and modifying it -----------------
   //
   arcsim::concurrent::ScopedLock lock(buf_mutex);
   
   // Read one character into buffer
   //
   int chrs = read (0, buf.ib_inbuf + buf.ib_write, 1);
   
   // If someone hit ESCAPE_CODE return to interactive console
   //
   if (buf.ib_inbuf[buf.ib_write] == ESCAPE_CODE) {
     // Hand back stdin and turn on interactive simulation mode
     //
     hand_back_stdin ();
     simInteractiveOn(sim_);
   } else {
     buf.ib_chars += chrs;
     buf.ib_write += chrs;
     if (buf.ib_write >= UART_INBUF_SIZE) {
       buf.ib_write -= UART_INBUF_SIZE;
     }
   }
   
   // Set the RX_EMPTY flag according to the buffer status,
   // and set/clear the interrupt line appropriately.
   //
   reg[0].status &= ~(RX_EMPTY);
   set_or_clear_interrupts (0);
 }
Пример #2
0
static int 
connect_t(int s, const struct sockaddr *name, socklen_t namelen, int timeout)
{
	int r, ofl;
	int s_error = 0;
	socklen_t optlen;

	/* set nonblocking */
	ofl = fcntl(s, F_GETFL, 0);
	fcntl(s, F_SETFL, ofl | O_NONBLOCK);

	/* connect */
	r = connect(s, name, namelen);

	if (r < 0 && errno == EINPROGRESS) {
	/* wait for timeout */
		r = poll_fd(s, timeout, POLLOUT);
		if (r == 0) {
			r = -1;
			errno = ETIMEDOUT;
		} else if (r > 0) {
			/* check errors on socket, e. g. ECONNREFUSED */
			optlen = sizeof(s_error);
			getsockopt(s, SOL_SOCKET, SO_ERROR, (void *)&s_error, &optlen);
			if (s_error) {
				r = -1;
				errno = s_error;
			}
		}
	}

	/* set blocking back */
	fcntl(s, F_SETFL, ofl);

	/* return */
	return r;
}
Пример #3
0
int command(int desc, const struct cl_node *root, const struct cl_limits *limits, int options, const struct cfgstruct *copt, int timeout)
{
	char buff[1025];
	int bread, opt, retval;
	struct cfgstruct *cpt;


    retval = poll_fd(desc, timeout);
    switch (retval) {
    case 0: /* timeout */
	return -2;
    case -1:
	mdprintf(desc, "ERROR\n");
	logg("!Command: poll_fd failed.\n");
	return -1;
    }

    while((bread = readsock(desc, buff, 1024)) == -1 && errno == EINTR);

    if(bread == 0) {
	/* Connection closed */
	return -1;
    }

    if(bread < 0) {
	logg("!Command parser: read() failed.\n");
	/* at least try to display this error message */
	/* mdprintf(desc, "ERROR: Command parser: read() failed.\n"); */
	return -1;
    }

    buff[bread] = 0;
    cli_chomp(buff);

    if(!strncmp(buff, CMD1, strlen(CMD1))) { /* SCAN */
	if(scan(buff + strlen(CMD1) + 1, NULL, root, limits, options, copt, desc, 0) == -2)
	    if(cfgopt(copt, "ExitOnOOM"))
		return COMMAND_SHUTDOWN;

    } else if(!strncmp(buff, CMD2, strlen(CMD2))) { /* RAWSCAN */
	opt = options & ~CL_SCAN_ARCHIVE;
	if(scan(buff + strlen(CMD2) + 1, NULL, root, NULL, opt, copt, desc, 0) == -2)
	    if(cfgopt(copt, "ExitOnOOM"))
		return COMMAND_SHUTDOWN;

    } else if(!strncmp(buff, CMD3, strlen(CMD3))) { /* QUIT */
	return COMMAND_SHUTDOWN;

    } else if(!strncmp(buff, CMD4, strlen(CMD4))) { /* RELOAD */
	mdprintf(desc, "RELOADING\n");
	return COMMAND_RELOAD;

    } else if(!strncmp(buff, CMD5, strlen(CMD5))) { /* PING */
	mdprintf(desc, "PONG\n");

    } else if(!strncmp(buff, CMD6, strlen(CMD6))) { /* CONTSCAN */
	if(scan(buff + strlen(CMD6) + 1, NULL, root, limits, options, copt, desc, 1) == -2)
	    if(cfgopt(copt, "ExitOnOOM"))
		return COMMAND_SHUTDOWN;

    } else if(!strncmp(buff, CMD7, strlen(CMD7))) { /* VERSION */
	    const char *dbdir;
	    char *path;
	    struct cl_cvd *daily;

	if((cpt = cfgopt(copt, "DatabaseDirectory")) || (cpt = cfgopt(copt, "DataDirectory")))
	    dbdir = cpt->strarg;
	else
	    dbdir = cl_retdbdir();

	if(!(path = mmalloc(strlen(dbdir) + 11))) {
	    mdprintf(desc, "Memory allocation error - SHUTDOWN forced\n");
	    return COMMAND_SHUTDOWN;
	}

	sprintf(path, "%s/daily.cvd", dbdir);

	if((daily = cl_cvdhead(path))) {
		time_t t = (time_t) daily->stime;

	    pthread_mutex_lock(&ctime_mutex);
	    mdprintf(desc, "ClamAV "VERSION"/%d/%s", daily->version, ctime(&t));
	    pthread_mutex_unlock(&ctime_mutex);
	    cl_cvdfree(daily);
	} else {
	    mdprintf(desc, "ClamAV "VERSION"\n");
	}

	free(path);

    } else if(!strncmp(buff, CMD8, strlen(CMD8))) { /* STREAM */
	if(scanstream(desc, NULL, root, limits, options, copt) == CL_EMEM)
	    if(cfgopt(copt, "ExitOnOOM"))
		return COMMAND_SHUTDOWN;

    } else if(!strncmp(buff, CMD9, strlen(CMD9))) { /* SESSION */
	return COMMAND_SESSION;

    } else if(!strncmp(buff, CMD10, strlen(CMD10))) { /* END */
	return COMMAND_END;

    } else if(!strncmp(buff, CMD11, strlen(CMD11))) { /* SHUTDOWN */
	return COMMAND_SHUTDOWN;

    } else if(!strncmp(buff, CMD12, strlen(CMD12))) { /* FD */
	    int fd = atoi(buff + strlen(CMD12) + 1);

	scanfd(fd, NULL, root, limits, options, copt, desc, 0);
	close(fd); /* FIXME: should we close it here? */

    } else {
	mdprintf(desc, "UNKNOWN COMMAND\n");
    }

    return 0; /* no error and no 'special' command executed */
}
Пример #4
0
int scanstream(int odesc, unsigned long int *scanned, const struct cl_engine *engine, unsigned int options, const struct optstruct *opts, char term)
{
	int ret, sockfd, acceptd;
	int tmpd, bread, retval, firsttimeout, timeout, btread;
	unsigned int port = 0, portscan, min_port, max_port;
	unsigned long int quota = 0, maxsize = 0;
	short bound = 0;
	const char *virname;
	char buff[FILEBUFF];
	char peer_addr[32];
	struct cb_context context;
	struct sockaddr_in server;
	struct sockaddr_in peer;
	socklen_t addrlen;
	char *tmpname;


    min_port = optget(opts, "StreamMinPort")->numarg;
    max_port = optget(opts, "StreamMaxPort")->numarg;

    /* search for a free port to bind to */
    port = cli_rndnum(max_port - min_port);
    bound = 0;
    for (portscan = 0; portscan < 1000; portscan++) {
	port = (port - 1) % (max_port - min_port + 1);

	memset((char *) &server, 0, sizeof(server));
	server.sin_family = AF_INET;
	server.sin_port = htons(min_port + port);
	server.sin_addr.s_addr = htonl(INADDR_ANY);

	if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
	    continue;

	if(bind(sockfd, (struct sockaddr *) &server, (socklen_t)sizeof(struct sockaddr_in)) == -1)
	    closesocket(sockfd);
	else {
	    bound = 1;
	    break;
	}
    }
    port += min_port;

    timeout = optget(opts, "ReadTimeout")->numarg;
    firsttimeout = optget(opts, "CommandReadTimeout")->numarg;

    if(!bound) {
	logg("!ScanStream: Can't find any free port.\n");
	mdprintf(odesc, "Can't find any free port. ERROR%c", term);
	return -1;
    } else {
	if (listen(sockfd, 1) == -1) {
        logg("!ScanStream: listen() error on socket. Error returned is %s.\n", strerror(errno));
        closesocket(sockfd);
        return -1;
    }
	if(mdprintf(odesc, "PORT %u%c", port, term) <= 0) {
	    logg("!ScanStream: error transmitting port.\n");
	    closesocket(sockfd);
	    return -1;
	}
    }

    retval = poll_fd(sockfd, firsttimeout, 0);
    if (!retval || retval == -1) {
	const char *reason = !retval ? "timeout" : "poll";
	mdprintf(odesc, "Accept %s. ERROR%c", reason, term);
	logg("!ScanStream %u: accept %s.\n", port, reason);
	closesocket(sockfd);
	return -1;
    }

    addrlen = sizeof(peer);
    if((acceptd = accept(sockfd, (struct sockaddr *) &peer, (socklen_t *)&addrlen)) == -1) {
	closesocket(sockfd);
	mdprintf(odesc, "accept() ERROR%c", term);
	logg("!ScanStream %u: accept() failed.\n", port);
	return -1;
    }

    *peer_addr = '\0';
    inet_ntop(peer.sin_family, &peer.sin_addr, peer_addr, sizeof(peer_addr));
    logg("*Accepted connection from %s on port %u, fd %d\n", peer_addr, port, acceptd);

    if(cli_gentempfd(optget(opts, "TemporaryDirectory")->strarg, &tmpname, &tmpd)) {
	shutdown(sockfd, 2);
	closesocket(sockfd);
	closesocket(acceptd);
	mdprintf(odesc, "cli_gentempfd() failed. ERROR%c", term);
	logg("!ScanStream(%s@%u): Can't create temporary file.\n", peer_addr, port);
	return -1;
    }

    quota = maxsize = optget(opts, "StreamMaxLength")->numarg;

    while((retval = poll_fd(acceptd, timeout, 0)) == 1) {
	/* only read up to max */
	btread = (maxsize && (quota < sizeof(buff))) ? quota : sizeof(buff);
	if (!btread) {
		logg("^ScanStream(%s@%u): Size limit reached (max: %lu)\n", peer_addr, port, maxsize);
		break; /* Scan what we have */
	}
	bread = recv(acceptd, buff, btread, 0);
	if(bread <= 0)
	    break;

	quota -= bread;

	if(writen(tmpd, buff, bread) != bread) {
	    shutdown(sockfd, 2);
	    closesocket(sockfd);
	    closesocket(acceptd);
	    mdprintf(odesc, "Temporary file -> write ERROR%c", term);
	    logg("!ScanStream(%s@%u): Can't write to temporary file.\n", peer_addr, port);
	    close(tmpd);
	    if(!optget(opts, "LeaveTemporaryFiles")->enabled)
		unlink(tmpname);
	    free(tmpname);
	    return -1;
	}
    }

    switch(retval) {
	case 0: /* timeout */
	    mdprintf(odesc, "read timeout ERROR%c", term);
	    logg("!ScanStream(%s@%u): read timeout.\n", peer_addr, port);
	    break;
	case -1:
	    mdprintf(odesc, "read poll ERROR%c", term);
	    logg("!ScanStream(%s@%u): read poll failed.\n", peer_addr, port);
	    break;
    }

    if(retval == 1) {
	lseek(tmpd, 0, SEEK_SET);
	thrmgr_setactivetask(peer_addr, NULL);
	context.filename = peer_addr;
	context.virsize = 0;
        context.scandata = NULL;
	ret = cl_scandesc_callback(tmpd, &virname, scanned, engine, options, &context);
	thrmgr_setactivetask(NULL, NULL);
    } else {
    	ret = -1;
    }
    close(tmpd);
    if(!optget(opts, "LeaveTemporaryFiles")->enabled)
	unlink(tmpname);
    free(tmpname);

    closesocket(acceptd);
    closesocket(sockfd);

    if(ret == CL_VIRUS) {
	if(context.virsize && optget(opts, "ExtendedDetectionInfo")->enabled) {
	    mdprintf(odesc, "stream: %s(%s:%llu) FOUND%c", virname, context.virhash, context.virsize, term);
	    logg("stream(%s@%u): %s(%s:%llu) FOUND\n", peer_addr, port, virname, context.virhash, context.virsize);
	} else {
	    mdprintf(odesc, "stream: %s FOUND%c", virname, term);
	    logg("stream(%s@%u): %s FOUND\n", peer_addr, port, virname);
	}
	virusaction("stream", virname, opts);
    } else if(ret != CL_CLEAN) {
    	if(retval == 1) {
	    mdprintf(odesc, "stream: %s ERROR%c", cl_strerror(ret), term);
	    logg("stream(%s@%u): %s ERROR\n", peer_addr, port, cl_strerror(ret));
	}
    } else {
	mdprintf(odesc, "stream: OK%c", term);
        if(logok)
	    logg("stream(%s@%u): OK\n", peer_addr, port); 
    }

    return ret;
}
Пример #5
0
static int 
spamdscan_socket(const char *file, const struct spamd_server *srv, struct config_file *cfg, rspamd_result_t *res)
{
#ifdef HAVE_PATH_MAX
	char buf[PATH_MAX + 10];
#elif defined(HAVE_MAXPATHLEN)
	char buf[MAXPATHLEN + 10];
#else
#error "neither PATH_MAX nor MAXPATHEN defined"
#endif
	char *c, *err;
	struct sockaddr_un server_un;
	struct sockaddr_in server_in;
	int s, r, fd, ofl, size = 0;
	struct stat sb;
	struct rspamd_metric_result *cur = NULL;
	struct rspamd_symbol *cur_symbol;

	/* somebody doesn't need reply... */
	if (!srv)
		return 0;

	if (srv->sock_type == AF_LOCAL) {

		memset(&server_un, 0, sizeof(server_un));
		server_un.sun_family = AF_UNIX;
		strncpy(server_un.sun_path, srv->sock.unix_path, sizeof(server_un.sun_path));

		if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
			msg_warn("spamd: socket %s, %s", srv->sock.unix_path,
					strerror (errno));
			return -1;
		}
		if (connect_t(s, (struct sockaddr *) & server_un, sizeof(server_un), cfg->spamd_connect_timeout) < 0) {
			msg_warn("spamd: connect %s, %s", srv->sock.unix_path,
					strerror (errno));
			close(s);
			return -1;
		}
	} else {
		/* inet hostname, send stream over tcp/ip */

		memset(&server_in, 0, sizeof(server_in));
		server_in.sin_family = AF_INET;
		server_in.sin_port = srv->sock.inet.port;
		memcpy((char *)&server_in.sin_addr, &srv->sock.inet.addr, sizeof(struct in_addr));

		if ((s = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
			msg_warn("spamd: socket %s", strerror (errno));
			return -1;
		}
		if (connect_t(s, (struct sockaddr *) & server_in, sizeof(server_in), cfg->spamd_connect_timeout) < 0) {
			msg_warn("spamd: connect %s, %s", srv->name, strerror (errno));
			close(s);
			return -1;
		}
	}
	/* Get file size */
	fd = open(file, O_RDONLY);
	if (fstat (fd, &sb) == -1) {
		msg_warn ("spamd: stat failed: %s", strerror (errno));
		close(s);
		return -1;
	}
	
	if (poll_fd(s, cfg->spamd_connect_timeout, POLLOUT) < 1) {
		msg_warn ("spamd: timeout waiting writing, %s", srv->name);
		close (s);
		return -1;
	}
	/* Set blocking again */
	ofl = fcntl(s, F_GETFL, 0);
	fcntl(s, F_SETFL, ofl & (~O_NONBLOCK));

	r = snprintf (buf, sizeof (buf), "SYMBOLS SPAMC/1.2\r\nContent-length: %ld\r\n\r\n", (long int)sb.st_size);
	if (write (s, buf, r) == -1) {
		msg_warn("spamd: write (%s), %s", srv->name, strerror (errno));
		close(fd);
		close(s);
		return -1;
	}

#ifdef HAVE_SENDFILE
#if defined(FREEBSD)
	if (sendfile(fd, s, 0, 0, 0, 0, 0) != 0) {
		msg_warn("spamd: sendfile (%s), %s", srv->name, strerror (errno));
		close(fd);
		close(s);
		return -1;
	}
#elif defined(LINUX)
	off_t off = 0;
	if (sendfile(s, fd, &off, sb.st_size) == -1) {
		msg_warn("spamd: sendfile (%s), %s", srv->name, strerror (errno));
		close(fd);
		close(s);
		return -1;		
	}
#endif
#else 
	while ((r = read (fd, buf, sizeof (buf))) > 0) {
		write (s, buf, r);
	}
#endif

	fcntl(s, F_SETFL, ofl);
	close(fd);

	/* wait for reply */

	if (poll_fd(s, cfg->spamd_results_timeout, POLLIN) < 1) {
		msg_warn("spamd: timeout waiting results %s", srv->name);
		close(s);
		return -1;
	}
	
	/*
	 * read results
	 */

	buf[0] = 0;

	while ((r = read(s, buf + size, sizeof (buf) - size - 1)) > 0 && size < sizeof (buf) - 1) {
		size += r;
	}
	buf[size] = 0;

	if (r < 0) {
		msg_warn("spamd: read, %s, %s", srv->name, strerror (errno));
		close(s);
		return -1;
	}

	close(s);

	/*
	 * ok, we got result; test what we got
	 */

	if ((c = strstr(buf, "Spam: ")) == NULL) {
		msg_warn("spamd: unexpected result on file (%s) %s, %s", srv->name, file, buf);
		return -2;
	}
	else {

		cur = malloc (sizeof (struct rspamd_metric_result));
		if (cur == NULL) {
			msg_err ("malloc falied: %s", strerror (errno));
			return -1;
		}
		bzero (cur, sizeof (struct rspamd_metric_result));
		/* Find mark */
		c = strchr (c, ';');
		if (c != NULL && *c != '\0') {
			cur->score = strtod (c + 1, &err);
			if (*err == ' ' && *(err + 1) == '/') {
				cur->required_score = strtod (err + 3, NULL);
			}
			else {
				cur->score = 0;
			}
		}
		else {
			cur->score = 0;
			cur->required_score = 0;
		}
	}

	/* Skip empty lines */
	while (*c && *c++ != '\n');
	while (*c++ && (*c == '\r' || *c == '\n'));
	/* Write symbols */
	if (*c != '\0') {
		err = strchr (c, '\r');
		if (err != NULL) {
			*err = '\0';
		}
		cur_symbol = malloc (sizeof (struct rspamd_symbol));
		cur_symbol->symbol = strdup (c);
		TAILQ_INSERT_HEAD(&cur->symbols, cur_symbol, entry);
	}

	if (strstr(buf, "True") != NULL) {
		cur->action = METRIC_ACTION_REJECT;
		return 1;
	}

	return 0;
}
Пример #6
0
static int 
rspamdscan_socket(SMFICTX *ctx, struct mlfi_priv *priv, const struct spamd_server *srv,
		struct config_file *cfg, rspamd_result_t *res, char **mid)
{
	char buf[16384];
	char *c, *p, *err_str;
	struct sockaddr_un server_un;
	struct sockaddr_in server_in;
	int s, r, fd, ofl, size = 0, to_write, written, state, next_state, toklen;
	int remain;
	struct stat sb;
	struct rspamd_metric_result *cur = NULL;
	struct rcpt *rcpt;
	struct rspamd_symbol *cur_symbol;

	/* somebody doesn't need reply... */
	if (!srv)
		return 0;

	if (srv->sock_type == AF_LOCAL) {

		memset(&server_un, 0, sizeof(server_un));
		server_un.sun_family = AF_UNIX;
		strncpy(server_un.sun_path, srv->sock.unix_path, sizeof(server_un.sun_path));

		if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
			msg_warn("rspamd: socket %s, %s", srv->sock.unix_path,
					strerror (errno));
			return -1;
		}
		if (connect_t(s, (struct sockaddr *) & server_un, sizeof(server_un), cfg->spamd_connect_timeout) < 0) {
			msg_warn("rspamd: connect %s, %s", srv->sock.unix_path,
					strerror (errno));
			close(s);
			return -1;
		}
	} else {
		/* inet hostname, send stream over tcp/ip */

		memset(&server_in, 0, sizeof(server_in));
		server_in.sin_family = AF_INET;
		server_in.sin_port = srv->sock.inet.port;
		memcpy((char *)&server_in.sin_addr, &srv->sock.inet.addr, sizeof(struct in_addr));

		if ((s = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
			msg_warn("rspamd: socket %s", strerror (errno));
			return -1;
		}
		if (connect_t(s, (struct sockaddr *) & server_in, sizeof(server_in), cfg->spamd_connect_timeout) < 0) {
			msg_warn("rspamd: connect %s: %s", srv->name, strerror (errno));
			close(s);
			return -1;
		}
	}
	/* Get file size */
	fd = open(priv->file, O_RDONLY);
	if (fstat (fd, &sb) == -1) {
		msg_warn ("rspamd: stat failed: %s", strerror (errno));
		close(s);
		return -1;
	}
	
	if (poll_fd(s, cfg->spamd_connect_timeout, POLLOUT) < 1) {
		msg_warn ("rspamd: timeout waiting writing, %s", srv->name);
		close (s);
		return -1;
	}
	/* Set blocking again */
	ofl = fcntl(s, F_GETFL, 0);
	fcntl(s, F_SETFL, ofl & (~O_NONBLOCK));
	
	r = 0;
	to_write = sizeof (buf) - r;
	written = snprintf (buf + r, to_write, "SYMBOLS RSPAMC/1.2\r\nContent-length: %ld\r\n", (long int)sb.st_size);
	if (written > to_write) {
		msg_warn("rspamd: buffer overflow while filling buffer (%s)", srv->name);
		close(fd);
		close(s);
		return -1;
	}
	r += written;

	for (rcpt = priv->rcpts.lh_first; rcpt != NULL; rcpt = rcpt->r_list.le_next) {
		to_write = sizeof (buf) - r;
		written = snprintf (buf + r, to_write, "Rcpt: %s\r\n", rcpt->r_addr);
		if (written > to_write) {
			msg_warn("rspamd: buffer overflow while filling buffer (%s)", srv->name);
			close(fd);
			close(s);
			return -1;
		}
		r += written;
	}

	if (priv->priv_from[0] != '\0') {
		to_write = sizeof (buf) - r;
		written = snprintf (buf + r, to_write, "From: %s\r\n", priv->priv_from);
		if (written > to_write) {
			msg_warn("rspamd: buffer overflow while filling buffer (%s)", srv->name);
			close(fd);
			close(s);
			return -1;
		}
		r += written;
	}
	if (priv->priv_helo[0] != '\0') {
		to_write = sizeof (buf) - r;
		written = snprintf (buf + r, to_write, "Helo: %s\r\n", priv->priv_helo);
		if (written > to_write) {
			msg_warn("rspamd: buffer overflow while filling buffer (%s)", srv->name);
			close(fd);
			close(s);
			return -1;
		}
		r += written;
	}
	if (priv->priv_hostname[0] != '\0' && memcmp (priv->priv_hostname, "unknown", 8) != 0) {
		to_write = sizeof (buf) - r;
		written = snprintf (buf + r, to_write, "Hostname: %s\r\n", priv->priv_hostname);
		if (written > to_write) {
			msg_warn("rspamd: buffer overflow while filling buffer (%s)", srv->name);
			close(fd);
			close(s);
			return -1;
		}
		r += written;
	}
	if (priv->priv_ip[0] != '\0') {
		to_write = sizeof (buf) - r;
		written = snprintf (buf + r, to_write, "IP: %s\r\n", priv->priv_ip);
		if (written > to_write) {
			msg_warn("rspamd: buffer overflow while filling buffer (%s)", srv->name);
			close(fd);
			close(s);
			return -1;
		}
		r += written;
	}
	if (priv->priv_user[0] != '\0') {
		to_write = sizeof (buf) - r;
		written = snprintf (buf + r, to_write, "User: %s\r\n", priv->priv_user);
		if (written > to_write) {
			msg_warn("rspamd: buffer overflow while filling buffer (%s)", srv->name);
			close(fd);
			close(s);
			return -1;
		}
		r += written;
	}
	to_write = sizeof (buf) - r;
	written = snprintf (buf + r, to_write, "Queue-ID: %s\r\n\r\n", priv->mlfi_id);
	if (written > to_write) {
		msg_warn("rspamd: buffer overflow while filling buffer (%s)", srv->name);
		close(fd);
		close(s);
		return -1;
	}
	r += written;



	if (write (s, buf, r) == -1) {
		msg_warn("rspamd: write (%s), %s", srv->name, strerror (errno));
		close(fd);
		close(s);
		return -1;
	}

#ifdef HAVE_SENDFILE
#if defined(FREEBSD)
	if (sendfile(fd, s, 0, 0, 0, 0, 0) != 0) {
		msg_warn("rspamd: sendfile (%s), %s", srv->name, strerror (errno));
		close(fd);
		close(s);
		return -1;
	}
#elif defined(LINUX)
	off_t off = 0;
	if (sendfile(s, fd, &off, sb.st_size) == -1) {
		msg_warn("rspamd: sendfile (%s), %s", srv->name, strerror (errno));
		close(fd);
		close(s);
		return -1;		
	}
#endif
#else 
	while ((r = read (fd, buf, sizeof (buf))) > 0) {
		write (s, buf, r);
	}
#endif

	fcntl(s, F_SETFL, ofl);
	close(fd);

	/* wait for reply */

	if (poll_fd(s, cfg->spamd_results_timeout, POLLIN) < 1) {
		msg_warn("rspamd: timeout waiting results %s", srv->name);
		close(s);
		return -1;
	}
	
	/*
	 * read results
	 */

	buf[0] = 0;
	size = 0;
	
	/* XXX: in fact here should be some FSM to parse reply and this one just skip long replies */
	while ((r = read(s, buf + size, sizeof (buf) - size - 1)) > 0 && size < sizeof (buf) - 1) {
		size += r;
	}

	if (r < 0) {
		msg_warn("rspamd: read, %s, %s", srv->name, strerror (errno));
		close(s);
		return -1;
	}
	buf[size] = '\0';
	close(s);

#define TEST_WORD(x)																\
do {																				\
	if (remain < sizeof ((x)) - 1 || memcmp (p, (x), sizeof ((x)) - 1) != 0) {		\
		msg_warn ("invalid reply from server %s at state %d, expected: %s, got %*s", srv->name, state, ((x)), (int)sizeof((x)), p);				\
		return -1;																	\
	}																				\
	p += sizeof((x)) - 1;															\
	remain -= sizeof((x)) - 1;														\
} while (0)


	c = buf;
	p = buf;
	remain = size - 1;
	state = 0;
	next_state = 100;

	while (remain > 0) {
		switch (state) {
			case 0:
				/*
				 * Expect first reply line:
				 * RSPAMD/{VERSION} {ERROR_CODE} {DESCR} CRLF
				 */
				TEST_WORD("RSPAMD/");
				if ((c = strchr (p, ' ')) == NULL) {
					msg_warn ("invalid reply from server %s on state %d", srv->name, state);
					return -1;
				}
				/* Well now in c we have space symbol, skip all */
				while (remain > 0 && isspace (*c)) {
					c ++;
				}
				/* Now check code */
				if (*c != '0') {
					msg_warn ("invalid reply from server %s on state %d, code: %c", srv->name, state, *c);
					return -1;
				}
				/* Now skip everything till \n */
				if ((c = strchr (c, '\n')) == NULL) {
					msg_warn ("invalid reply from server %s on state %d", srv->name, state);
					return -1;
				}
				c ++;
				remain -= c - p;
				p = c;
				next_state = 2;
				state = 99;
				break;
			case 2:
				/*
				 * In this state we compare begin of line with Metric:
				 */
				TEST_WORD("Metric:");
				cur = malloc (sizeof (struct rspamd_metric_result));
				if (cur == NULL) {
					msg_err ("malloc failed: %s", strerror (errno));
					return -1;
				}
				cur->subject = NULL;
				TAILQ_INIT(&cur->symbols);
				next_state = 3;
				state = 99;
				break;
			case 3:
				/* 
				 * In this state we parse metric line 
				 * Typical line looks as name; result; score1 / score2[ / score3] and we are interested in:
				 * name, result, score1 and score2
				 */
				if ((c = strchr (p, ';')) == NULL) {
					msg_warn ("invalid reply from server %s on state %d, at position: %s", srv->name, state, p);
					return -1;
				}
				/* Now in c we have end of name and in p - begin of name, so copy this data to temp buffer */
				cur->metric_name = malloc (c - p + 1);
				if (cur->metric_name == NULL) {
					msg_err ("malloc failed: %s", strerror (errno));
					return -1;
				}
				rmilter_strlcpy (cur->metric_name, p, c - p + 1);
				remain -= c - p + 1;
				p = c + 1;
				/* Now skip result from rspamd, just extract 2 numbers */
				if ((c = strchr (p, ';')) == NULL) {
					msg_warn ("invalid reply from server %s on state %d, at position: %s", srv->name, state, p);
					return -1;
				}
				remain -= c - p + 1;
				p = c + 1;
				/* Now skip spaces */
				while (isspace (*p) && remain > 0) {
					p ++;
					remain --;
				}
				/* Try to read first mark */
				cur->score = strtod (p, &err_str);
				if (err_str != NULL && (*err_str != ' ' && *err_str != '/')) {
					msg_warn ("invalid reply from server %s on state %d, error converting score number: %s", srv->name, state, err_str);
					return -1;
				}
				remain -= err_str - p;
				p = err_str;
				while (remain > 0 && (*p == ' ' || *p == '/')) {
					remain --;
					p ++;
				}
				/* Try to read second mark */
				cur->required_score = strtod (p, &err_str);
				if (err_str != NULL && (*err_str != ' ' && *err_str != '/' && *err_str != '\r')) {
					msg_warn ("invalid reply from server %s on state %d, error converting required score number: %s", srv->name, state, err_str);
					return -1;
				}
				remain -= err_str - p;
				p = err_str;
				while (remain > 0 && *p != '\n') {
					remain --;
					p ++;
				}
				state = 99;
				next_state = 4;
				break;
			case 4:
				/* Symbol/Action */
				if (remain >= sizeof ("Symbol:") && memcmp (p, "Symbol:", sizeof ("Symbol:") - 1) == 0) {
					state = 99;
					next_state = 5;
					p += sizeof("Symbol:") - 1;															\
					remain -= sizeof("Symbol:") - 1;
				}
				else if (remain >= sizeof ("Action:") && memcmp (p, "Action:", sizeof ("Action:") - 1) == 0) {
					state = 99;
					next_state = 6;
					p += sizeof("Action:") - 1;															\
					remain -= sizeof("Action:") - 1;
				}
				else if (remain >= sizeof ("Metric:") && memcmp (p, "Metric:", sizeof ("Metric:") - 1) == 0) {
					state = 99;
					next_state = 3;
					p += sizeof("Metric:") - 1;															\
					remain -= sizeof("Metric:") - 1;
					TAILQ_INSERT_HEAD(res, cur, entry);
					cur = malloc (sizeof (struct rspamd_metric_result));
					if (cur == NULL) {
						msg_err ("malloc failed: %s", strerror (errno));
						return -1;
					}
					TAILQ_INIT(&cur->symbols);
				}
				else if (remain >= sizeof ("Message-ID:") && memcmp (p, "Message-ID:", sizeof ("Message-ID:") - 1) == 0) {
					state = 99;
					next_state = 7;
					p += sizeof("Message-ID:") - 1;															\
					remain -= sizeof("Message-ID:") - 1;
				}
				else if (remain >= sizeof ("Subject:") && memcmp (p, "Subject:", sizeof ("Subject:") - 1) == 0) {
					state = 99;
					next_state = 8;
					p += sizeof("Subject:") - 1;															\
					remain -= sizeof("Subject:") - 1;
				}
				else {
					toklen = strcspn (p, "\r\n");
					if (toklen > remain) {
						msg_info ("bad symbol name detected");
						return -1;
					}
					remain -= toklen;
					p += toklen;
					next_state = 4;
					state = 99;
				}
				break;
			case 5:
				/* Parse symbol line */
				toklen = strcspn (p, ";\r\n");
				if (toklen == 0 || toklen > remain) {
					/* Bad symbol name */
					msg_info ("bad symbol name detected");
					return -1;
				}
				cur_symbol = malloc (sizeof (struct rspamd_symbol));
				if (cur_symbol == NULL) {
					msg_err ("malloc failed: %s", strerror (errno));
					return -1;
				}
				cur_symbol->symbol = malloc (toklen + 1);
				if (cur_symbol->symbol == NULL) {
					msg_err ("malloc failed: %s", strerror (errno));
					return -1;
				}
				rmilter_strlcpy (cur_symbol->symbol, p, toklen + 1);
				TAILQ_INSERT_HEAD (&cur->symbols, cur_symbol, entry);
				/* Skip to the end of line */
				toklen = strcspn (p, "\r\n");
				if (toklen > remain) {
					msg_info ("bad symbol name detected");
					return -1;
				}
				remain -= toklen;
				p += toklen;
				next_state = 4;
				state = 99;
				break;
			case 6:
				/* Parse action */
				if (memcmp (p, "reject", sizeof ("reject") - 1) == 0) {
					cur->action = METRIC_ACTION_REJECT;
				}
				else if (memcmp (p, "greylist", sizeof ("greylist") - 1) == 0) {
					cur->action = METRIC_ACTION_GREYLIST;
				}
				else if (memcmp (p, "add header", sizeof ("add header") - 1) == 0) {
					cur->action = METRIC_ACTION_ADD_HEADER;
				}
				else if (memcmp (p, "rewrite subject", sizeof ("rewrite subject") - 1) == 0) {
					cur->action = METRIC_ACTION_REWRITE_SUBJECT;
				}
				else {
					cur->action = METRIC_ACTION_NOACTION;
				}
				/* Skip to the end of line */
				toklen = strcspn (p, "\r\n");
				if (toklen > remain) {
					msg_info ("bad symbol name detected");
					return -1;
				}
				remain -= toklen;
				p += toklen;
				next_state = 4;
				state = 99;
				break;
			case 7:
				/* Parse message id */
				toklen = strcspn (p, "\r\n");
				*mid = malloc (toklen + 1);
				rmilter_strlcpy (*mid, p, toklen + 1);
				remain -= toklen;
				p += toklen;
				next_state = 4;
				state = 99;
				break;
			case 8:
				/* Parse subject line */
				toklen = strcspn (p, "\r\n");
				if (cur) {
					cur->subject = malloc (toklen + 1);
					rmilter_strlcpy (cur->subject, p, toklen + 1);
				}
				remain -= toklen;
				p += toklen;
				next_state = 4;
				state = 99;
				break;
			case 99:
				/* Skip spaces */
				if (isspace (*p)) {
					p ++;
					remain --;
				}
				else {
					state = next_state;
				}
				break;
			default:
				msg_err ("state machine breakage detected, state = %d, p = %s", state, p);
				return -1;
		}
	}

	if (cur != NULL) {
		TAILQ_INSERT_HEAD(res, cur, entry);
	}
	return 0;
}