+
+static const char *is_quad(const char *q)
+/* Check if the string passed in points to what could be one quad of a
+ * dotted-quad IP address. Requirements are that the string is not a
+ * NULL pointer, begins with a period (which is skipped) or a digit
+ * and ends with a period or a NULL. If these requirements are met, a
+ * pointer to the last character (the period or the NULL character) is
+ * returned; otherwise NULL.
+ */
+{
+ const char *r;
+
+ if (!q || !*q)
+ return NULL;
+ if (*q == '.')
+ q++;
+ for(r=q;isdigit((unsigned char)*r);r++)
+ ;
+ if ( ((*r) && (*r != '.')) || ((r-q) < 1) || ((r-q)>3) )
+ return NULL;
+ /* Make sure quad is < 255 */
+ if ( (r-q) == 3)
+ {
+ if (*q > '2')
+ return NULL;
+ else if (*q == '2')
+ {
+ if (*(q+1) > '5')
+ return NULL;
+ else if (*(q+1) == '5')
+ {
+ if (*(q+2) > '5')
+ return NULL;
+ }
+ }
+ }
+ return r;
+}
+
+static int is_dottedquad(const char *hostname)
+/* Returns a true value if the passed in string looks like an IP
+ * address in dotted-quad form, and a false value otherwise.
+ */
+
+{
+ return ((hostname=is_quad(is_quad(is_quad(is_quad(hostname))))) != NULL) &&
+ (*hostname == '\0');
+}
+
+static int open_smtp_sink(struct query *ctl, struct msgblk *msg,
+ int *good_addresses, int *bad_addresses /* this must be signed, to prevent endless loop in from_addresses */)
+/* open an SMTP stream */
+{
+ const char *ap;
+ struct idlist *idp;
+ char options[MSGBUFSIZE];
+ char addr[HOSTLEN+USERNAMELEN+1];
+#ifdef EXPLICIT_BOUNCE_ON_BAD_ADDRESS
+ char **from_responses;
+#endif /* EXPLICIT_BOUNCE_ON_BAD_ADDRESS */
+ int total_addresses;
+ int force_transient_error = 0;
+ int smtp_err;
+
+ /*
+ * Compute ESMTP options.
+ */
+ options[0] = '\0';
+ if (ctl->server.esmtp_options & ESMTP_8BITMIME) {
+ if (ctl->pass8bits || (ctl->mimemsg & MSG_IS_8BIT))
+ strcpy(options, " BODY=8BITMIME");
+ else if (ctl->mimemsg & MSG_IS_7BIT)
+ strcpy(options, " BODY=7BIT");
+ }
+
+ if ((ctl->server.esmtp_options & ESMTP_SIZE) && msg->reallen > 0)
+ sprintf(options + strlen(options), " SIZE=%d", msg->reallen);
+
+ /*
+ * Try to get the SMTP listener to take the Return-Path
+ * address as MAIL FROM. If it won't, fall back on the
+ * remotename and mailserver host. This won't affect replies,
+ * which use the header From address anyway; the MAIL FROM
+ * address is a place for the SMTP listener to send
+ * bouncemail. The point is to guarantee a FQDN in the MAIL
+ * FROM line -- some SMTP listeners, like smail, become
+ * unhappy otherwise.
+ *
+ * RFC 1123 requires that the domain name part of the
+ * MAIL FROM address be "canonicalized", that is a
+ * FQDN or MX but not a CNAME. We'll assume the Return-Path
+ * header is already in this form here (it certainly
+ * is if rewrite is on). RFC 1123 is silent on whether
+ * a nonexistent hostname part is considered canonical.
+ *
+ * This is a potential problem if the MTAs further upstream
+ * didn't pass canonicalized From/Return-Path lines, *and* the
+ * local SMTP listener insists on them.
+ *
+ * Handle the case where an upstream MTA is setting a return
+ * path equal to "@". Ghod knows why anyone does this, but
+ * it's been reported to happen in mail from Amazon.com and
+ * Motorola.
+ *
+ * Also, if the hostname is a dotted quad, wrap it in square brackets.
+ * Apparently this is required by RFC2821, section 4.1.3.
+ */
+ if (!msg->return_path[0] || (msg->return_path[0] == '@'))
+ {
+ if (strchr(ctl->remotename,'@') || strchr(ctl->remotename,'!'))
+ {
+ snprintf(addr, sizeof(addr), "%s", ctl->remotename);
+ }
+ else if (is_dottedquad(ctl->server.truename))
+ {
+ snprintf(addr, sizeof(addr), "%s@[%s]", ctl->remotename,
+ ctl->server.truename);
+ }
+ else
+ {
+ snprintf(addr, sizeof(addr),
+ "%s@%s", ctl->remotename, ctl->server.truename);
+ }
+ ap = addr;
+ }
+ else if (strchr(msg->return_path,'@') || strchr(msg->return_path,'!'))
+ ap = msg->return_path;
+ /* in case Return-Path was "<>" we want to preserve that */
+ else if (strcmp(msg->return_path,"<>") == 0)
+ ap = msg->return_path;
+ else /* in case Return-Path existed but was local */
+ {
+ if (is_dottedquad(ctl->server.truename))
+ {
+ snprintf(addr, sizeof(addr), "%s@[%s]", msg->return_path,
+ ctl->server.truename);
+ }
+ else
+ {
+ snprintf(addr, sizeof(addr), "%s@%s",
+ msg->return_path, ctl->server.truename);
+ }
+ ap = addr;
+ }
+
+ if ((smtp_err = SMTP_from(ctl->smtp_socket, ctl->smtphostmode,
+ ap, options)) == SM_UNRECOVERABLE)
+ {
+ smtp_close(ctl, 0);
+ return(PS_TRANSIENT);
+ }
+ if (smtp_err != SM_OK)
+ {
+ int err = handle_smtp_report(ctl, msg); /* map to PS_TRANSIENT or PS_REFUSED */
+
+ smtp_rset(ctl); /* stay on the safe side */
+ return(err);
+ }
+
+ /*
+ * Now list the recipient addressees
+ */
+ total_addresses = 0;
+ for (idp = msg->recipients; idp; idp = idp->next)
+ total_addresses++;
+#ifdef EXPLICIT_BOUNCE_ON_BAD_ADDRESS
+ from_responses = (char **)xmalloc(sizeof(char *) * total_addresses);
+#endif /* EXPLICIT_BOUNCE_ON_BAD_ADDRESS */
+ for (idp = msg->recipients; idp; idp = idp->next)
+ if (idp->val.status.mark == XMIT_ACCEPT)