+ "%s no useable spamd server addresses in spamd_address configuration option.",
+ loglabel);
+ goto defer;
+ }
+
+ current_server = spamd_get_server(spamd_address_vector, num_servers);
+ sd = spamd_address_vector[current_server];
+ for(;;)
+ {
+ uschar * errstr;
+
+ DEBUG(D_acl) debug_printf_indent("spamd: trying server %s\n", sd->hostspec);
+
+ for (;;)
+ {
+ /*XXX could potentially use TFO early-data here */
+ if ( (spamd_sock = ip_streamsocket(sd->hostspec, &errstr, 5)) >= 0
+ || sd->retry <= 0
+ )
+ break;
+ DEBUG(D_acl) debug_printf_indent("spamd: server %s: retry conn\n", sd->hostspec);
+ while (sd->retry > 0) sd->retry = sleep(sd->retry);
+ }
+ if (spamd_sock >= 0)
+ break;
+
+ log_write(0, LOG_MAIN, "%s spamd: %s", loglabel, errstr);
+ sd->is_failed = TRUE;
+
+ current_server = spamd_get_server(spamd_address_vector, num_servers);
+ if (current_server < 0)
+ {
+ log_write(0, LOG_MAIN|LOG_PANIC, "%s all spamd servers failed", loglabel);
+ goto defer;
+ }
+ sd = spamd_address_vector[current_server];
+ }
+ }
+
+(void)fcntl(spamd_sock, F_SETFL, O_NONBLOCK);
+/* now we are connected to spamd on spamd_sock */
+if (sd->is_rspamd)
+ {
+ gstring * req_str;
+ const uschar * s;
+
+ req_str = string_append(NULL, 8,
+ "CHECK RSPAMC/1.3\r\nContent-length: ", string_sprintf("%lu\r\n", mbox_size),
+ "Queue-Id: ", message_id,
+ "\r\nFrom: <", sender_address,
+ ">\r\nRecipient-Number: ", string_sprintf("%d\r\n", recipients_count));
+
+ for (i = 0; i < recipients_count; i ++)
+ req_str = string_append(req_str, 3,
+ "Rcpt: <", recipients_list[i].address, ">\r\n");
+ if ((s = expand_string(US"$sender_helo_name")) && *s)
+ req_str = string_append(req_str, 3, "Helo: ", s, "\r\n");
+ if ((s = expand_string(US"$sender_host_name")) && *s)
+ req_str = string_append(req_str, 3, "Hostname: ", s, "\r\n");
+ if (sender_host_address)
+ req_str = string_append(req_str, 3, "IP: ", sender_host_address, "\r\n");
+ if ((s = expand_string(US"$authenticated_id")) && *s)
+ req_str = string_append(req_str, 3, "User: ", s, "\r\n");
+ req_str = string_catn(req_str, "\r\n", 2);
+ wrote = send(spamd_sock, req_str->s, req_str->ptr, 0);
+ }
+else
+ { /* spamassassin variant */
+ (void)string_format(spamd_buffer,
+ sizeof(spamd_buffer),
+ "REPORT SPAMC/1.2\r\nUser: %s\r\nContent-length: %ld\r\n\r\n",
+ user_name,
+ mbox_size);
+ /* send our request */
+ wrote = send(spamd_sock, spamd_buffer, Ustrlen(spamd_buffer), 0);
+ }
+
+if (wrote == -1)
+ {
+ (void)close(spamd_sock);
+ log_write(0, LOG_MAIN|LOG_PANIC,
+ "%s spamd %s send failed: %s", loglabel, callout_address, strerror(errno));
+ goto defer;
+ }
+
+/* now send the file */
+/* spamd sometimes accepts connections but doesn't read data off
+ * the connection. We make the file descriptor non-blocking so
+ * that the write will only write sufficient data without blocking
+ * and we poll the descriptor to make sure that we can write without
+ * blocking. Short writes are gracefully handled and if the whole
+ * transaction takes too long it is aborted.
+ * Note: poll() is not supported in OSX 10.2 and is reported to be
+ * broken in more recent versions (up to 10.4).
+ */