アプリ版:「スタンプのみでお礼する」機能のリリースについて

VC++2005,openssl、Win7 で作業しています。

Gメールのサーバーに POP3S で接続して
メールを取り出したいと思っています。

とりあえず、サーバーからは

+OK Gpop ready for requset from ......

と返事が来ました。
POP3S での、サーバーとのやりとりについて
解説してある資料を探しています。

ご存知の方よろしくご指導下さい。
お願いいたします。

A 回答 (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 …


まず、ブロッキングで書くとしたら、サーバーから返ってくる文字列をちゃんと確認して、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 を繰り返さないといけないことがありますので。

この回答への補足

ありがとうございます。
なんとか、サーバーとの通信が出来ました。

サーバからのレスポンス
+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;
}


ありがとうございました。

補足日時:2013/07/26 09:17
    • good
    • 0
この回答へのお礼

ありがとうございました。
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;
}

お礼日時:2013/07/26 09:34

>と同じことを、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()ではまだデータが無い。とかいうことも。

この回答への補足

ネットで探したものをテストしています。

// 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;
}

補足日時:2013/07/25 23:32
    • good
    • 0

>POP3S での、サーバーとのやりとりについて


>解説してある資料を探しています。

POP3S用の特別なコマンドがあるワケではないので…探してもないんじゃないですかね?
・接続用の標準ポート番号が異なる。
・SSLで通信路が保護される。
だけで後の手順は通常のPOP3と一緒だったかと。

平文で接続して、途中からSSL通信に移行する。
とかいう場合はその限りではありませんが。
http://salt.iajapan.org/wpmu/anti_spam/admin/tec …
のSTLSコマンドとか。
    • good
    • 0
この回答へのお礼

コマンドラインでの操作

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/CIPIN …

お礼日時:2013/07/25 20:06

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!