Cold Water

#RFC

To see what condition my Exim was in...

Es war wie verhext. Mein Mailserver arbeitete soweit einwandfrei, der Server war nicht überlastet, die meisten Mails kamen auch problemlos durch, und doch...

Und doch gab es Probleme. Manche Absender beschwerten sich, dass es ziemlich lange dauert, bis mein Mailserver eine E-Mail annimmt. Bei wenigen lieferte deren Mailserver sogar einen Bounce, dass mein Mailserver gerade nicht erreichbar sei und es noch ein paar Stunden lang probiert werden würde. Die Mail kam dann erst Stunden später bei mir an.

Des Rätsels Lösung ist eine RFC1413-konforme Voreinstellung vom Exim. Der schickt nämlich bei jeder Anfrage erst mal eine “ident”-Anfrage an den Absender zurück. Und je nachdem, wie dicht die Firewall des Absenders ist, wird diese Anfrage verworfen und nie beantwortet. Der Exim wartet dann erst mal 30 Sekunden lang, bevor er dem Absender überhaupt seine Aufmerksamkeit schenkt.

Bei manchen Absendern war der MTA wiederum so eingestellt, dass er bereits nach weniger als 30 Sekunden ohne Lebenszeichen von meinem Server aufgibt und die E-Mail später zuzustellen versucht.

Die maßgebliche Option in der exim.conf lautet "rfc1413_query_timeout". Dieser ist auf 30 Sekunden voreingestellt. Folgende Zeile schaltet die ident-Rückfrage ganz aus:

rfc1413_query_timeout = 0s

Seitdem läuft mein Mailserver endlich reibungslos.

Technisch gesehen lag die Ursache nicht wirklich an meinem Server, sondern an der Firewall des Absenders. Dort sollte wenigstens ein Fake-Identd-Service laufen, oder eine Verbindungsanfrage an Port 113 nicht einfach verworfen, sondern zurückgewiesen werden.

Die ident-Anfrage ist nicht wirklich sinnlos. Sie ermöglicht es in manchen Fällen, den wahren Absender einer E-Mail zu ermitteln. Wer das Feature also behalten möchte, kann auch einen kleineren Wert wie “10s” oder “1s” einstellen.

RFC-2047-Wandler

Dieser Codeschnippsel wandelt einen String nach RFC-2047 um.

Die RFC-2047 wird verwendet, um Umlaute in Mail- und News-Headern darzustellen. Die folgenden beiden Funktionen wandeln einen String entsprechend um.

/**
 * Converts the passed string to an RFC-2047 encoded word.
 * 
 * The string will always be converted. This function also takes care of
 * proper wrapping.
 *
 * @param   $str      String to be encoded
 * @param   $encode   The character set to be used (e.g. "ISO-8859-1")
 * @return  Converted string
 */
function rfc2047conv($str, $encode) {
  if(!strlen($str)) return "";
  $result  = "=?${encode}?Q?";
  $intro   = strlen($result);
  $charcnt = $intro;
  for($ix=0; $ix < strlen($str); $ix++) {
    if($charcnt > 70) {
      $result .= "?=\r\n =?${encode}Q?";
      $charcnt = $intro;
    }
    $chr = $str{$ix};
    if($chr=='?' || $chr=='_' || $chr=='=' || $chr<' ' || $chr>'~' ) {
      $result .= sprintf("=%02X", ord($chr));
      $charcnt+=3;
    }else {
      $result .= ($chr==' ' ? "_" : $chr);
      $charcnt++;
    }
  }
  $result .= "?=";
  return $result;
}

/**
 * This function will only encode the shortest required section of
 * the string. If there are no characters in the string that need to
 * be encoded, the string will be returned unaltered.
 * 
 * WARNING: Even though most mail clients can handle the result, it
 * is not RFC compliant!
 *
 * @param   $str      String to be encoded
 * @param   $encode   The character set to be used
 * @return  Converted string
 */
function rfc2047short($str, $encode) {
  return preg_replace(
    '/^([ -~]*)(.*?)([ -~]*)$/e',
    '"$1".rfc2047conv("$2",$encode)."$3"',
    $str
  );
}