gmailのsmtpを使うには?

解決


null  2009-07-23 23:56:45  No: 70654  IP: [192.*.*.*]

最近ネットワークプログラミングについて勉強し始めた者なのですが、SMTPプログラミングをしていて、「SMTPサーバとしてsmtp.gmail.comが使える」ようなことを聞いたので、実際にやってみようと思ったのですが、いざ通信するとなると、サーバからはいつも502エラーが返されて、うまくいきません。ググってみても、(自分の理解力が足りないせいかもしれませんが)恥ずかしながらなにがいけないのかよくわかりませんでした。どなたかわかり易く原因と対処法などを教えていただけませんでしょうか?

ソースコードは下のとおりです(まずはお手本通りということで某参考書の付録ソースコードにちょっと手を加えただけです。)

#include <stdio.h>
#include <winsock2.h>
#include <mbstring.h>

int MySend(SOCKET, char *);
int MyRecvAndPrint(SOCKET);
void MySJisToJis(unsigned char *, unsigned char *);

int main()
{
  WSADATA wsaData;
  LPHOSTENT lpHost;
  SOCKET s;
  SOCKADDR_IN sockadd;
  char szServer[256], szStr[1024], jisStr[1024];
  char szFrom[64], szTo[64], szPort[8];
  u_short port;
  unsigned int addr;

  if (WSAStartup(MAKEWORD(1, 1), &wsaData) != 0) {
    printf("WSAStartupエラーです\n");
    return -1;
  }
  strcpy(szServer, "smtp.gmail.com");
  strcpy(szPort, "587");
  port = (u_short)atoi(szPort);
  strcpy(szFrom, "[送信元]@gmail.com");
  strcpy(szTo, "[送信先]@gmail.com");

  lpHost = gethostbyname(szServer);
  if (lpHost == NULL) {
    addr = inet_addr(szServer);
    lpHost = gethostbyaddr((char *)&addr, 4, AF_INET);
    wsprintf(szStr, "%sが見つかりません\n", szServer);
    printf(szStr);
    return -2;
  }
  s = socket(PF_INET, SOCK_STREAM, 0);
  if (s == INVALID_SOCKET) {
    printf("ソケットをオープンできません\n");
    return -3;
  }
  sockadd.sin_family = AF_INET;
  sockadd.sin_port = htons(port);
  sockadd.sin_addr = *((LPIN_ADDR)*lpHost->h_addr_list);
  if (connect(s, (PSOCKADDR)&sockadd, sizeof(sockadd)) != 0) {
    printf("サーバーソケットに接続失敗\n");
    closesocket(s);
    WSACleanup();
    return -4;
  } 
  if (MyRecvAndPrint(s)) {
    closesocket(s);
    WSACleanup();
    return -5;
  }

  wsprintf(szStr, "HELO %s", szServer);
  if (MySend(s, szStr)) {
    perror("HELO失敗\n");
    closesocket(s);
    WSACleanup();
    return -6;
  }
  if (MyRecvAndPrint(s)) {
    closesocket(s);
    WSACleanup();
    return -7;
  }

  wsprintf(szStr, "MAIL FROM:<%s>", szFrom);
  if (MySend(s, szStr)) {
    perror("FROM失敗\n");
    closesocket(s);
    WSACleanup();
    return -8;
  }
  if (MyRecvAndPrint(s)) {
    closesocket(s);
    WSACleanup();
    return -9;
  }

  wsprintf(szStr, "RCPT TO:<%s>", szTo);
  if (MySend(s, szStr)) {
    closesocket(s);
    WSACleanup();
    return -10;
  }
  if (MyRecvAndPrint(s)) {
    closesocket(s);
    WSACleanup();
    return -11;
  }

  wsprintf(szStr, "DATA");
  if (MySend(s, szStr)) {
    closesocket(s);
    WSACleanup();
    return -12;
  }
  if (MyRecvAndPrint(s)) {
    closesocket(s);
    WSACleanup();
    return -13;
  }
  if (MySend(s, "X-Mailer:Nekodemo-Wakaru Mailer Ver.0.5")) {
      closesocket(s);
      WSACleanup();
      return -14;
  }
  
  if (MySend(s, "Subject:Test Mail")) {
      closesocket(s);
      WSACleanup();
      return -15;
  }
  send(s, "\r\n", 2, 0);
  while (1) {
    printf("メール本文を1行ずつ入力します。終了時はピリオドのみ入力\n");
    gets(szStr);
    MySJisToJis((unsigned char *)szStr, (unsigned char *)jisStr); 
    if (MySend(s, jisStr)) {
      closesocket(s);
      WSACleanup();
      return -17;
    }
    if (strcmp(szStr, ".") == 0)
      break;
  }
  if (MyRecvAndPrint(s)) {
    closesocket(s);
    WSACleanup();
    return -18;
  }
  MySend(s, "QUIT");
  if (MyRecvAndPrint(s)) {
    closesocket(s);
    WSACleanup();
    return -19;
  }
  
  closesocket(s);
  WSACleanup();
  printf("通信を終了しました\n");
  return 0;
}

int MySend(SOCKET s, char *lpszBuf)
{
  int nRtn;
  char szBuf[1024];

  wsprintf(szBuf, "%s\r\n", lpszBuf);
  nRtn = send(s, szBuf, (int)strlen(szBuf), 0);
  if (nRtn == SOCKET_ERROR) {
    perror("sendエラーです\n");
    return -1;
  }
  return 0;
}

int MyRecvAndPrint(SOCKET s)
{
  int nRtn;
  char szBuf[1024];

  memset(szBuf, '\0', sizeof(szBuf));
  nRtn = recv(s, szBuf, (int)sizeof(szBuf) - 1, 0);
  if (nRtn == SOCKET_ERROR) {
    perror("recv失敗です\n");
    return -1;
  }
  printf("%s", szBuf);
  return 0;
}

void MySJisToJis(unsigned char *lpszOrg, unsigned char *lpszDest)
{
    int i = 0, iR = 0, c;
    BOOL bSTART = FALSE;

    while (1) {
        if (_ismbblead(lpszOrg[i]) && bSTART == FALSE) {
            lpszDest[iR] = 0x1b;
            lpszDest[iR + 1] = 0x24;
            lpszDest[iR + 2] = 0x42;
            c = MAKEWORD(lpszOrg[i + 1], lpszOrg[i]);
            lpszDest[iR + 3] = (unsigned char)HIBYTE(_mbcjmstojis(c));
            lpszDest[iR + 4] = (unsigned char)LOBYTE(_mbcjmstojis(c));
            bSTART = TRUE;
            i += 2;
            iR += 5;
            if (!_ismbblead(lpszOrg[i])) {
                lpszDest[iR] = 0x1b;
                lpszDest[iR + 1] = 0x28;
                lpszDest[iR + 2] = 0x42;
                bSTART = FALSE;
                iR += 3;
            }
            continue;
        }
        if (_ismbblead(lpszOrg[i]) && bSTART) {
            c = MAKEWORD(lpszOrg[i + 1], lpszOrg[i]);
            lpszDest[iR] = (unsigned char)HIBYTE(_mbcjmstojis(c));
            lpszDest[iR + 1] = (unsigned char)LOBYTE(_mbcjmstojis(c));
            i += 2;
            iR += 2;
            if (!_ismbblead(lpszOrg[i])) {
                lpszDest[iR] = 0x1b;
                lpszDest[iR + 1] = 0x28;
                lpszDest[iR + 2] = 0x42;
                bSTART = FALSE;
                iR += 3;
            }
            continue;
        }
        if (lpszOrg[i] == '\r') {
            lpszDest[iR] = '\n';
            i += 2;
            iR++;
            continue;
        }
        if (lpszOrg[i] == '\0') {
            lpszDest[iR] = lpszOrg[i];
            break;
        }
        lpszDest[iR] = lpszOrg[i];
        iR++;
        i++;
    }
    return;
}

編集 削除
subaru  2009-07-24 00:40:21  No: 70655  IP: [192.*.*.*]

プログラムを書く前にtelnetなどで直接コマンド叩いて
通信できるか確認してみては?

編集 削除
aetos  2009-07-24 09:57:13  No: 70656  IP: [192.*.*.*]

502エラーが出るのは何をした時(コード中のどこ)なのですか?

編集 削除
null  2009-07-24 12:57:04  No: 70657  IP: [192.*.*.*]

------------------------------------------------------------------
pingコマンド(smtp.gmail.com):成功
------------------------------------------------------------------
Pinging gmail-smtp-msa.l.google.com [209.85.147.109] with 32 bytes of data:

Reply from 209.85.147.109: bytes=32 time=98ms TTL=240
Reply from 209.85.147.109: bytes=32 time=97ms TTL=240
Reply from 209.85.147.109: bytes=32 time=98ms TTL=240
Reply from 209.85.147.109: bytes=32 time=98ms TTL=240

Ping statistics for 209.85.147.109:
    Packets: Sent = 4, Received = 4, Lost = 0 (0% loss),
Approximate round trip times in milli-seconds:
    Minimum = 97ms, Maximum = 98ms, Average = 97ms
------------------------------------------------------------------


------------------------------------------------------------------
telnetコマンド(telnet smtp.gmail.com 587):失敗
------------------------------------------------------------------
220 mx.google.com ESMTP k35sm5167934waf.53
[処理が終わらない]
------------------------------------------------------------------


------------------------------------------------------------------
telnetコマンド(telnet 209.85.147.109 587):失敗
------------------------------------------------------------------
220 mx.google.com ESMTP n9sm5237592wag.58
[処理が終わらない]
------------------------------------------------------------------


------------------------------------------------------------------
(自プログラム)
------------------------------------------------------------------
ソケット接続時の返事のみ220
(220 mx.google.com ESMTP [ランダムな半角英数字の羅列])
HELOコマンドから以下ずっと502
(502 5.5.1 Unrecognized command. [220のときと同じ文字列])
------------------------------------------------------------------

編集 削除
YuO  2009-07-24 13:24:13  No: 70658  IP: [192.*.*.*]

HELLOコマンドではなくExtended HELLOコマンド (EHLO) を使ってみてはどうですか。
502は実装されていないという意味なのですから。

一応,RFC 2821のF.3にはサーバーは引き続きサポートしろ (MUST) とは書いてありますが,所詮はAppendix部分ですから。

あと,RFC 2821は読みましょう。
http://www.ietf.org/rfc/rfc2821.txt
Appendix.Dに,典型的なシナリオが書いてあります。

編集 削除
subaru  2009-07-24 13:32:26  No: 70659  IP: [192.*.*.*]

>------------------------------------------------------------------
>telnetコマンド(telnet smtp.gmail.com 587):失敗
>------------------------------------------------------------------
>220 mx.google.com ESMTP k35sm5167934waf.53
>[処理が終わらない]

これは接続できたので次のコマンド待ちの状態。
プログラムどおりならこのあとHELOを打ち込みます。

ちょっと試してみると、HELOでもEHLOでも
250が返されたので原因は別にあるかもしれません。

編集 削除
オショウ  2009-07-24 14:12:12  No: 70660  IP: [192.*.*.*]

回答ではありません。

http://mail.google.com/support/bin/answer.py?hl=jp&answer=13287
gmailは、SSL認証なので、コードを見る限りプレーンテキストで
ID/PWDを送信しようとされてますが、当然そのようなエラーにな
るのでは?

SSL認証のコードを書く必要があるかと。

※  クライアントで SMTP 認証がサポートされていない場合は、
    Gmail メールへのアクセスはできません。と書かれてます!

以上。

編集 削除
オショウ  2009-07-24 14:14:11  No: 70661  IP: [192.*.*.*]

追伸・・・

C#なら
http://prog.re-d.net/2009/05/02/gmail%E3%82%92%E4%BD%BF%E3%81%A3%E3%81%9F%E3%83%A1%E3%83%BC%E3%83%AB%E9%80%81%E4%BF%A1-c.php

こんなに簡単にできます。

C++/CLI で、.NETのクラス使ったら?

以上。

編集 削除
null  2009-08-01 19:06:06  No: 70662  IP: [192.*.*.*]

>SSL認証のコードを書く必要があるかと。

いろいろ調べてみたのですが、コーディングの仕方がまったく分かりません…
どなたか教えていただけないでしょうか…

編集 削除
オショウ  2009-08-01 19:43:50  No: 70663  IP: [192.*.*.*]

検索してみた?

http://www.example-code.com/mfc/ssl_client.asp

中身、未確認ですが・・・

参考まで。

編集 削除
null  2009-08-01 22:43:15  No: 70664  IP: [192.*.*.*]

素早いご回答ありがとうございます。
もうすこし頑張ってみます。



…英語はできないと駄目ですか…

編集 削除
null  2009-08-01 23:16:17  No: 70665  IP: [192.*.*.*]

>http://www.example-code.com/mfc/ssl_client.asp

ここを参考に一応解決いたしました。
ありがとうございました。

編集 削除