目次

qmail-remote に SMTP-AUTH対応させる(SMTPクライアントになりきってもらう)

2008年07月22日 23時32分22秒

これは何?

これは、qmailの構成プログラムである qmail-remote にSMTP-AUTH対応させた際のメモ。 OSは FreeBSD/amd64 7.0RELEASE 、qmailはportsで導入した qmail-1.03_7 です。

ISPがSMTPを潰しました

まぁ、去年・一昨年くらいから「停止するるよぅ」っていってましたから。 一応メールを出して確認したのですがヘルプデスクの回答は、「SMTP-AUTH対応のサーバに切り替えてくれ」しか書いてない。 こちらの質問に全く答えてないし。 この回答は予想通りではあるけど、もうちっときちんとした回答できないかなぁ。

やるべき事

こちらは、室内にqmailを使ったメールサーバがあり、室内から出すメールはこのメールサーバがISPのSMTPサーバへメールを転送していました。 このISPのSMTPサーバが使用不能となり、新たな SMTP-AUTH対応の SMTPサーバを利用する必要があります。

しかし、室内のqmailは素のqmailなので、SMTP-AUTH に対応していません。

qmail-remoteへのパッチ

今回やりたいことは、お部屋のqmailにSMTP-AUTHをおしゃべりさせてISPのメールサーバに接続できる様にする事です。 お部屋のqmailがメール送信の際にユーザに対してアカウントを要求する、という話とは全く違うのでご注意を。 グーグル先生にお伺いを立てたところ、 http://search.luky.org/linux-users.a/msg02384.html を通して http://tomclegg.net/qmail/#qmail-smtpd-auth が見つかりました。 グーグル様さまです。

http://tomclegg.net/qmail/#qmail-smtpd-auth の説明どおりにオペレーションを行うと、残念ですがパッチはあたりません。 このパッチは素のqmailに対してのものなので、portsで展開された他のパッチが当たったソースには適用できません。 ただ、内容を見た限りだとそれほど大量なパッチではないので、手作業で修正してみました。

ソース qmail-remote.c は以下の様になります。

 chopin# pwd
 /usr/ports/mail/qmail/work/qmail-1.03
 chopin#
 chopin# diff -c qmail-remote.c.orig qmail-remote.c
 *** qmail-remote.c.orig 2008-07-22 21:55:44.000000000 +0900
 --- qmail-remote.c      2008-07-22 22:34:02.000000000 +0900
 ***************
 *** 45,50 ****
 --- 45,52 ----
   struct constmap maproutes;
   stralloc host = {0};
   stralloc sender = {0};
 + stralloc auth_smtp_user = {0};
 + stralloc auth_smtp_pass = {0};
 
   saa reciplist = {0};
 
 ***************
 *** 225,246 ****
     unsigned long code;
     int flagbother;
     int i;
 
     code = smtpcode();
     if (code >= 500 && code < 600) quit("DConnected to "," but greeting failed");
     if (code >= 400 && code < 500) return; /* try next MX, see RFC-2821 */
     if (code != 220) quit("ZConnected to "," but greeting failed");
 
 !   substdio_puts(&smtpto,"HELO ");
     substdio_put(&smtpto,helohost.s,helohost.len);
     substdio_puts(&smtpto,"\r\n");
     substdio_flush(&smtpto);
 !   if (smtpcode() != 250) quit("ZConnected to "," but my name was rejected");
 !
 !   substdio_puts(&smtpto,"MAIL FROM:<");
 !   substdio_put(&smtpto,sender.s,sender.len);
 !   substdio_puts(&smtpto,">\r\n");
 !   substdio_flush(&smtpto);
     code = smtpcode();
     if (code >= 500) quit("DConnected to "," but sender was rejected");
     if (code >= 400) quit("ZConnected to "," but sender was rejected");
 --- 227,284 ----
     unsigned long code;
     int flagbother;
     int i;
 +   stralloc slop = {0};
 
     code = smtpcode();
     if (code >= 500 && code < 600) quit("DConnected to "," but greeting failed");
     if (code >= 400 && code < 500) return; /* try next MX, see RFC-2821 */
     if (code != 220) quit("ZConnected to "," but greeting failed");
 
 !   substdio_puts(&smtpto,"EHLO ");
     substdio_put(&smtpto,helohost.s,helohost.len);
     substdio_puts(&smtpto,"\r\n");
     substdio_flush(&smtpto);
 !   if (smtpcode() != 250) {
 !     substdio_puts(&smtpto,"HELO ");
 !     substdio_put(&smtpto,helohost.s,helohost.len);
 !     substdio_puts(&smtpto,"\r\n");
 !     substdio_flush(&smtpto);
 !     if (smtpcode() != 250) quit("ZConnected to "," but my name was rejected");
 !   }
 !   i = 0;
 !   while((i += str_chr(smtptext.s+i,'\n') + 1) && (i+14 < smtptext.len) &&
 !         str_diffn(smtptext.s+i+4,"AUTH LOGIN\n",11) &&
 !         str_diffn(smtptext.s+i+4,"AUTH LOGIN ",11) &&
 !         str_diffn(smtptext.s+i+4,"AUTH PLAIN LOGIN\n",17) &&
 !         str_diffn(smtptext.s+i+4,"AUTH PLAIN LOGIN ",17) &&
 !         str_diffn(smtptext.s+i+4,"AUTH=LOGIN\n",11) &&
 !         str_diffn(smtptext.s+i+4,"AUTH=LOGIN ",11));
 !   if ((i+14 < smtptext.len) && auth_smtp_user.len && auth_smtp_pass.len)  {
 !     substdio_puts(&smtpto,"AUTH LOGIN\r\n");
 !     substdio_flush(&smtpto);
 !     if (smtpcode() != 334) quit("ZConnected to "," but authentication was rejected (AUTH LOGIN)");
 !     if (b64encode(&auth_smtp_user,&slop) < 0) temp_nomem();
 !     substdio_put(&smtpto,slop.s,slop.len);
 !     substdio_puts(&smtpto,"\r\n");
 !     substdio_flush(&smtpto);
 !     if (smtpcode() != 334) quit("ZConnected to "," but authentication was rejected (username)");
 !     if (b64encode(&auth_smtp_pass,&slop) < 0) temp_nomem();
 !     substdio_put(&smtpto,slop.s,slop.len);
 !     substdio_puts(&smtpto,"\r\n");
 !     substdio_flush(&smtpto);
 !     if (smtpcode() != 235) quit("ZConnected to "," but authentication was rejected (password)");
 !     substdio_puts(&smtpto,"MAIL FROM:<");
 !     substdio_put(&smtpto,sender.s,sender.len);
 !     substdio_puts(&smtpto,"> AUTH=<");
 !     substdio_put(&smtpto,sender.s,sender.len);
 !     substdio_puts(&smtpto,">\r\n");
 !     substdio_flush(&smtpto);
 !   } else {
 !     substdio_puts(&smtpto,"MAIL FROM:<");
 !     substdio_put(&smtpto,sender.s,sender.len);
 !     substdio_puts(&smtpto,">\r\n");
 !     substdio_flush(&smtpto);
 !   }
     code = smtpcode();
     if (code >= 500) quit("DConnected to "," but sender was rejected");
     if (code >= 400) quit("ZConnected to "," but sender was rejected");
 ***************
 *** 445,451 ****
   char **argv;
   {
     static ipalloc ip = {0};
 !   int i;
     unsigned long random;
     char **recips;
     unsigned long prefme;
 --- 483,489 ----
   char **argv;
   {
     static ipalloc ip = {0};
 !   int i,j;
     unsigned long random;
     char **recips;
     unsigned long prefme;
 ***************
 *** 461,466 ****
 --- 499,507 ----
 
     if (!stralloc_copys(&host,argv[1])) temp_nomem();
 
 +   if (!stralloc_copys(&auth_smtp_user,"")) temp_nomem();
 +   if (!stralloc_copys(&auth_smtp_pass,"")) temp_nomem();
 +
     relayhost = 0;
     for (i = 0;i <= host.len;++i)
       if ((i == 0) || (i == host.len) || (host.s[i] == '.'))
 ***************
 *** 469,474 ****
 --- 510,525 ----
     if (relayhost && !*relayhost) relayhost = 0;
 
     if (relayhost) {
 +     i = str_chr(relayhost,' ');
 +     if (relayhost[i]) {
 +       j = str_chr(relayhost + i + 1,' ');
 +       if (relayhost[j]) {
 +       relayhost[i] = 0;
 +       relayhost[i + j + 1] = 0;
 +       if (!stralloc_copys(&auth_smtp_user,relayhost + i + 1)) temp_nomem();
 +       if (!stralloc_copys(&auth_smtp_pass,relayhost + i + j + 2)) temp_nomem();
 +       }
 +     }
       i = str_chr(relayhost,':');
       if (relayhost[i]) {
         scan_ulong(relayhost + i + 1,&smtp_port);

Makefileはこんな感じ。

 chopin# pwd
 /usr/ports/mail/qmail/work/qmail-1.03
 chopin#
 chopin# diff -c Makefile.orig Makefile
 *** Makefile.orig       2008-07-22 21:55:44.000000000 +0900
 --- Makefile    2008-07-22 22:07:22.000000000 +0900
 ***************
 *** 1502,1513 ****
   load qmail-remote.o control.o constmap.o timeoutread.o timeoutwrite.o \
   timeoutconn.o tcpto.o now.o dns.o ip.o ipalloc.o strsalloc.o ipme.o quote.o \
   ndelay.a case.a sig.a open.a lock.a seek.a getln.a stralloc.a alloc.a \
 ! substdio.a error.a str.a fs.a auto_qmail.o dns.lib socket.lib
         ./load qmail-remote control.o constmap.o timeoutread.o \
         timeoutwrite.o timeoutconn.o tcpto.o now.o dns.o ip.o \
         ipalloc.o strsalloc.o ipme.o quote.o ndelay.a case.a sig.a open.a \
         lock.a seek.a getln.a stralloc.a alloc.a substdio.a error.a \
 !       str.a fs.a auto_qmail.o  `cat dns.lib` `cat socket.lib`
 
   qmail-remote.0: \
   qmail-remote.8
 --- 1502,1513 ----
   load qmail-remote.o control.o constmap.o timeoutread.o timeoutwrite.o \
   timeoutconn.o tcpto.o now.o dns.o ip.o ipalloc.o strsalloc.o ipme.o quote.o \
   ndelay.a case.a sig.a open.a lock.a seek.a getln.a stralloc.a alloc.a \
 ! substdio.a error.a str.a fs.a auto_qmail.o base64.o dns.lib socket.lib
         ./load qmail-remote control.o constmap.o timeoutread.o \
         timeoutwrite.o timeoutconn.o tcpto.o now.o dns.o ip.o \
         ipalloc.o strsalloc.o ipme.o quote.o ndelay.a case.a sig.a open.a \
         lock.a seek.a getln.a stralloc.a alloc.a substdio.a error.a \
 !       str.a fs.a auto_qmail.o base64.o  `cat dns.lib` `cat socket.lib`
 
   qmail-remote.0: \
   qmail-remote.8

smtproutesへの記述

パッチを当てた事で、ファイル smtproutes の記述形式がちょっぴり変わります。 例えば、

:smtp.hogehoge.jp

の smtp.hogehoge.jp が廃止され、asmtp.hogehoge.jp となり、ポート 784 を指定する必要が出た場合、

:asmtp.hogehoge.jp:784

ユーザID,パスワードも必要な場合は、

:asmtp.hogehoge.jp:784 userid password

となります。