// 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); }
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; }
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 */ }
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; }
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; }
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; }