• ベストアンサー

POP3S

VC++2005,openssl、Win7 で作業しています。 Gメールのサーバーに POP3S で接続して メールを取り出したいと思っています。 とりあえず、サーバーからは +OK Gpop ready for requset from ...... と返事が来ました。 POP3S での、サーバーとのやりとりについて 解説してある資料を探しています。 ご存知の方よろしくご指導下さい。 お願いいたします。

質問者が選んだベストアンサー

  • ベストアンサー
回答No.3

SSL以前の問題ですね。 > while (1) { > char buf[1024] = {0}; > if (SSL_read(ssl, buf, sizeof(buf)-1) <= 0) > break; > res += buf; > } よくこれでブロックしないですね。 SSL_readでタイムアウトして切断されるまで待たされて、タイムアウトした所でSSL_read < 0となってループを抜ける事になりそうですが。 このコードだと、POP3Sではなく普通にread/writeを使ってPOP3を話すプログラムを書いても動かないと思います。 もし、「ブロック???」と思うなら、次のページでも読んでみると良いでしょう。 http://msdn.microsoft.com/ja-jp/library/3tbz7kf5%28v=vs.80%29.aspx まず、ブロッキングで書くとしたら、サーバーから返ってくる文字列をちゃんと確認して、CRLF (\r\n)が来ていた場合はループから抜けるようにしましょう。 例えばこんな感じ? size_t crlf_pos; while (1) { char buf2[1024] = {0}; if ((crlf_pos = res.find("\r\n")) != string::npos || SSL_read(ssl, buf2, sizeof(buf2)-1) <= 0) break; res += buf2; } cout << res.substr(0, crlf_pos) << endl; res.erase(0, crlf_pos + 2); // 2 = sizeof(CRLF) あと、POP3の資料といえばこの2つですよね。 http://tools.ietf.org/html/rfc2595 http://tools.ietf.org/html/rfc1939 RFC2595はPOP3をどうやってSSLと一緒に使うかの解説で、#1さんの回答にあったSTLSが説明されています。RFC1939はPOP3そのものの解説です。 あと、SSLで安全な通信がしたかったら、certificateをセットして、CRLかOCSPかをチェックしてください。なりすまし対策がないSSLは見掛け倒しです。暗号化通信をしているかもしれませんが、誰と暗号化通信をしているかはわかりません。 #2の回答についてコメントです > WSAAsyncSelect()でメッセージで通知して貰うようにして組んだことはありますが、 > FD_READで受信した後でSSL_read()しても何も読めない。という状況になったことはあります。 もしノンブロッキングなソケットが裏にあるとしたら、SSL_ERROR_WANT_READ / SSL_ERROR_WANT_WRITE をちゃんとハンドリングしないとダメですね。 http://www.openssl.org/docs/ssl/SSL_read.html selectで読めると言われても、SSLのレベルでデータが読めるまでには何度も read/write を繰り返さないといけないことがありますので。

uyama33
質問者

お礼

ありがとうございました。 LIST も確認できました。 // SSLtest.cpp : コンソール アプリケーションのエントリ ポイントを定義します。 // #include "stdafx.h" #include <iostream> #include <string> #include <winsock2.h> #include <conio.h> #include <ctype.h> #include "openssl\ssl.h" #include "openssl\crypto.h" #include "openssl\err.h" #include "openssl\rand.h" #pragma comment(lib, "ws2_32.lib") #pragma comment(lib, "libeay32.lib") #pragma comment(lib, "ssleay32.lib") int main(void) { WSADATA wsaData; struct sockaddr_in server; SOCKET sock; std::string req; // リクエスト std::string res; // レスポンス std::string host_url = "pop.googlemail.com"; req = ""; SSL *ssl; SSL_CTX *ctx; // Winsockの設定 WSAStartup(MAKEWORD(2, 0), &wsaData); sock = socket(AF_INET, SOCK_STREAM, 0); server.sin_family = AF_INET; server.sin_port = htons(995); server.sin_addr.S_un.S_addr = inet_addr(host_url.c_str()); if (server.sin_addr.S_un.S_addr == 0xffffffff) { struct hostent *host; unsigned int **addrptr; host = gethostbyname(host_url.c_str()); if (host == NULL) { return 1; } addrptr = (unsigned int **)host->h_addr_list; while (*addrptr != NULL) { server.sin_addr.S_un.S_addr = *(*addrptr); if (connect(sock, (struct sockaddr *)&server, sizeof(server)) == 0) { break; } } if (*addrptr == NULL) { return 1; } } else { if (connect(sock, (struct sockaddr *)&server, sizeof(server)) != 0) return 1; } SSL_load_error_strings(); SSL_library_init(); ctx = SSL_CTX_new(TLSv1_method()); if (ctx == NULL) { return 1; } ssl = SSL_new(ctx); if (ssl == NULL) { return 1; } if (SSL_set_fd(ssl, sock) == 0) { return 1; } RAND_poll(); while (RAND_status() == 0) { unsigned short rand_ret = rand() % 65536; RAND_seed(&rand_ret, sizeof(rand_ret)); } if (SSL_connect(ssl) != 1) { ERR_print_errors_fp(stderr); return 1; } SSL_write(ssl, req.c_str(), req.length()); std::cout << "サーバからのレスポンス" << std::endl; size_t crlf_pos; while (1) { char buf2[1024] = {0}; if ((crlf_pos = res.find("\r\n")) != std::string::npos || SSL_read(ssl, buf2, sizeof(buf2)-1) <= 0) break; res += buf2; } std::cout << res.substr(0, crlf_pos) << std::endl; res.erase(0, crlf_pos + 2); // 2 = sizeof(CRLF) std::cout << res << std::endl; req = "USER ******\r\n"; res = ""; SSL_write(ssl, req.c_str(), req.length()); std::cout << "サーバからのレスポンス" << std::endl; while (1) { char buf2[1024] = {0}; if ((crlf_pos = res.find("\r\n")) != std::string::npos || SSL_read(ssl, buf2, sizeof(buf2)-1) <= 0) break; res += buf2; } std::cout << res.substr(0, crlf_pos) << std::endl; res.erase(0, crlf_pos + 2); // 2 = sizeof(CRLF) std::cout << res << std::endl; res = ""; req = "PASS *******\r\n"; SSL_write(ssl, req.c_str(), req.length()); std::cout << "サーバからのレスポンス" << std::endl; while (1) { char buf2[1024] = {0}; if ((crlf_pos = res.find("\r\n")) != std::string::npos || SSL_read(ssl, buf2, sizeof(buf2)-1) <= 0) break; res += buf2; } std::cout << res.substr(0, crlf_pos) << std::endl; res.erase(0, crlf_pos + 2); // 2 = sizeof(CRLF) std::cout << res << std::endl; res = ""; req = "LIST\r\n"; SSL_write(ssl, req.c_str(), req.length()); std::cout << "サーバからのレスポンス" << std::endl; while (1) { char buf2[1024] = {0}; if ((crlf_pos = res.find("\r\n")) != std::string::npos || SSL_read(ssl, buf2, sizeof(buf2)-1) <= 0) break; res += buf2; } std::cout << res.substr(0, crlf_pos) << std::endl; res.erase(0, crlf_pos + 2); // 2 = sizeof(CRLF) std::cout << res << std::endl; SSL_shutdown(ssl); SSL_free(ssl); SSL_CTX_free(ctx); ERR_free_strings(); closesocket(sock); WSACleanup(); return 0; }

uyama33
質問者

補足

ありがとうございます。 なんとか、サーバーとの通信が出来ました。 サーバからのレスポンス +OK Gpop ready for requests from 203.138.226.165 yw8pf6263343pac.0 サーバからのレスポンス +OK send PASS サーバからのレスポンス -ERR [AUTH] Username and password not accepted. これは、USER と PASS が合っていないのでそうなっています。 Welcom まで来ました。 とりあえず修正したコードは以下のものです。 // SSLtest.cpp : コンソール アプリケーションのエントリ ポイントを定義します。 // #include "stdafx.h" #include <iostream> #include <string> #include <winsock2.h> #include <conio.h> #include <ctype.h> #include "openssl\ssl.h" #include "openssl\crypto.h" #include "openssl\err.h" #include "openssl\rand.h" #pragma comment(lib, "ws2_32.lib") #pragma comment(lib, "libeay32.lib") #pragma comment(lib, "ssleay32.lib") int main(void) { WSADATA wsaData; struct sockaddr_in server; SOCKET sock; std::string req; // リクエスト std::string res; // レスポンス std::string host_url = "pop.googlemail.com"; req = ""; SSL *ssl; SSL_CTX *ctx; // Winsockの設定 WSAStartup(MAKEWORD(2, 0), &wsaData); sock = socket(AF_INET, SOCK_STREAM, 0); server.sin_family = AF_INET; server.sin_port = htons(995); server.sin_addr.S_un.S_addr = inet_addr(host_url.c_str()); if (server.sin_addr.S_un.S_addr == 0xffffffff) { struct hostent *host; unsigned int **addrptr; host = gethostbyname(host_url.c_str()); if (host == NULL) { return 1; } addrptr = (unsigned int **)host->h_addr_list; while (*addrptr != NULL) { server.sin_addr.S_un.S_addr = *(*addrptr); if (connect(sock, (struct sockaddr *)&server, sizeof(server)) == 0) { break; } } if (*addrptr == NULL) { return 1; } } else { if (connect(sock, (struct sockaddr *)&server, sizeof(server)) != 0) return 1; } SSL_load_error_strings(); SSL_library_init(); ctx = SSL_CTX_new(TLSv1_method()); if (ctx == NULL) { return 1; } ssl = SSL_new(ctx); if (ssl == NULL) { return 1; } if (SSL_set_fd(ssl, sock) == 0) { return 1; } RAND_poll(); while (RAND_status() == 0) { unsigned short rand_ret = rand() % 65536; RAND_seed(&rand_ret, sizeof(rand_ret)); } if (SSL_connect(ssl) != 1) { ERR_print_errors_fp(stderr); return 1; } SSL_write(ssl, req.c_str(), req.length()); std::cout << "サーバからのレスポンス" << std::endl; size_t crlf_pos; while (1) { char buf2[1024] = {0}; if ((crlf_pos = res.find("\r\n")) != std::string::npos || SSL_read(ssl, buf2, sizeof(buf2)-1) <= 0) break; res += buf2; } std::cout << res.substr(0, crlf_pos) << std::endl; res.erase(0, crlf_pos + 2); // 2 = sizeof(CRLF) std::cout << res << std::endl; req = "USER ***********\r\n"; res = ""; SSL_write(ssl, req.c_str(), req.length()); std::cout << "サーバからのレスポンス" << std::endl; while (1) { char buf2[1024] = {0}; if ((crlf_pos = res.find("\r\n")) != std::string::npos || SSL_read(ssl, buf2, sizeof(buf2)-1) <= 0) break; res += buf2; } std::cout << res.substr(0, crlf_pos) << std::endl; res.erase(0, crlf_pos + 2); // 2 = sizeof(CRLF) std::cout << res << std::endl; res = ""; req = "PASS *********\r\n"; SSL_write(ssl, req.c_str(), req.length()); std::cout << "サーバからのレスポンス" << std::endl; while (1) { char buf2[1024] = {0}; if ((crlf_pos = res.find("\r\n")) != std::string::npos || SSL_read(ssl, buf2, sizeof(buf2)-1) <= 0) break; res += buf2; } std::cout << res.substr(0, crlf_pos) << std::endl; res.erase(0, crlf_pos + 2); // 2 = sizeof(CRLF) std::cout << res << std::endl; SSL_shutdown(ssl); SSL_free(ssl); SSL_CTX_free(ctx); ERR_free_strings(); closesocket(sock); WSACleanup(); return 0; } ありがとうございました。

その他の回答 (2)

  • Wr5
  • ベストアンサー率53% (2173/4061)
回答No.2

>と同じことを、SSL_write と SSL_read を使ってやりたいのですがうまく出来ません。 send()とrecv()をSSL_write()とSSL_read()に置き換えるだけなんですけどね。 # もちろんその前にSSL接続に必要な準備とかありますが。 んで……ブロッキングモードでやっているんでしょうか? ノンブロッキングモードでやっているんでしょうか? WSAAsyncSelect()でメッセージで通知して貰うようにして組んだことはありますが、 FD_READで受信した後でSSL_read()しても何も読めない。という状況になったことはあります。 おそらく、受信した生パケットがSSLの電文を複合するのにサイズが足りなかった…のかも知れませんが。 普通にrecv()で戻り値が0なら切断された…という処理をすることになりますが… SSL接続時の場合だとFD_READの後で読み出せるだけのデータが無いとか、SSLネゴシエーションのパケットを受信した為SSL_read()ではまだデータが無い。とかいうことも。

uyama33
質問者

補足

ネットで探したものをテストしています。 // SSLtest.cpp : コンソール アプリケーションのエントリ ポイントを定義します。 // #include "stdafx.h" #include <iostream> #include <string> #include <winsock2.h> #include <conio.h> #include <ctype.h> #include "openssl\ssl.h" #include "openssl\crypto.h" #include "openssl\err.h" #include "openssl\rand.h" #pragma comment(lib, "ws2_32.lib") #pragma comment(lib, "libeay32.lib") #pragma comment(lib, "ssleay32.lib") int main(void) { WSADATA wsaData; struct sockaddr_in server; SOCKET sock; std::string req; // リクエスト std::string res; // レスポンス std::string host_url = "pop.googlemail.com"; req = "USER *****\r\n\r\n"; SSL *ssl; SSL_CTX *ctx; // Winsockの設定 WSAStartup(MAKEWORD(2, 0), &wsaData); sock = socket(AF_INET, SOCK_STREAM, 0); server.sin_family = AF_INET; server.sin_port = htons(995); server.sin_addr.S_un.S_addr = inet_addr(host_url.c_str()); if (server.sin_addr.S_un.S_addr == 0xffffffff) { struct hostent *host; unsigned int **addrptr; host = gethostbyname(host_url.c_str()); if (host == NULL) { return 1; } addrptr = (unsigned int **)host->h_addr_list; while (*addrptr != NULL) { server.sin_addr.S_un.S_addr = *(*addrptr); if (connect(sock, (struct sockaddr *)&server, sizeof(server)) == 0) { break; } } if (*addrptr == NULL) { return 1; } } else { if (connect(sock, (struct sockaddr *)&server, sizeof(server)) != 0) return 1; } SSL_load_error_strings(); SSL_library_init(); ctx = SSL_CTX_new(TLSv1_method()); if (ctx == NULL) { return 1; } ssl = SSL_new(ctx); if (ssl == NULL) { return 1; } if (SSL_set_fd(ssl, sock) == 0) { return 1; } RAND_poll(); while (RAND_status() == 0) { unsigned short rand_ret = rand() % 65536; RAND_seed(&rand_ret, sizeof(rand_ret)); } if (SSL_connect(ssl) != 1) { ERR_print_errors_fp(stderr); return 1; } SSL_write(ssl, req.c_str(), req.length()); std::cout << "サーバからのレスポンス" << std::endl; while (1) { char buf[1024] = {0}; if (SSL_read(ssl, buf, sizeof(buf)-1) <= 0) break; res += buf; } std::cout << res << std::endl; res = ""; req = "PASS ******\r\n\r\n"; SSL_write(ssl, req.c_str(), req.length()); std::cout << "サーバからのレスポンス" << std::endl; while (1) { char buf2[1024] = {0}; if (SSL_read(ssl, buf2, sizeof(buf2)-1) <= 0) break; res += buf2; } std::cout << res << std::endl; SSL_shutdown(ssl); SSL_free(ssl); SSL_CTX_free(ctx); ERR_free_strings(); closesocket(sock); WSACleanup(); return 0; }

  • Wr5
  • ベストアンサー率53% (2173/4061)
回答No.1

>POP3S での、サーバーとのやりとりについて >解説してある資料を探しています。 POP3S用の特別なコマンドがあるワケではないので…探してもないんじゃないですかね? ・接続用の標準ポート番号が異なる。 ・SSLで通信路が保護される。 だけで後の手順は通常のPOP3と一緒だったかと。 平文で接続して、途中からSSL通信に移行する。 とかいう場合はその限りではありませんが。 http://salt.iajapan.org/wpmu/anti_spam/admin/tech/explanation/tls-arc/ のSTLSコマンドとか。

uyama33
質問者

お礼

コマンドラインでの操作 openssl s_client -connect pop.gmail.com:995 CONNECTED(00000003) [※SSL接続の出力は省略] --- +OK Gpop ready for requests from xxx.xx.xx.xxx x20xx1401750xxx USER [俺のid] +OK send PASS PASS [俺のpass] +OK Welcome. LIST +OK 5 messages (24576 bytes) 1 4699 2 4315 3 6260 と同じことを、SSL_write と SSL_read を使ってやりたいのですがうまく出来ません。 +OK Gpop ready for requests from xxx.xx.xx.xxx x20xx1401750xxx のあと、動かなくなります。 出来ましたらアドバイスお願いいたします。 参考: http://d.hatena.ne.jp/inuz/20070502/p1 http://www-cms.phys.s.u-tokyo.ac.jp/~naoki/CIPINTRO/NETWORK/openssl.html