SSブログ

Gmailにつなぐための実証コード - 最終的に作ったコード [プログラミング]

元々のC言語のソースのインデントが、スペースがふたつになっていてRubyみたい。Linux的に言うと、インデントのtabは8とかなんだろうけど、私はインデントのタブを、普通に4にしている人間です。やっぱり、いろいろな状況で見ると、普通は4tabぐらいが妥当なのかな、と思っています。というか、8タブって空白が多過ぎて、気持ち悪いんですが。C言語のインデントが2というのも悪くはないけど、見るの慣れてないので気になる。気になる程度で気持ち悪くはない。結局、Windowsから入った人間だからなぁ。

ええと、Gmailにつなぐために、OpenSSLのライブラリをC言語で使ってみた。なるべくミニマム設定でやったつもり。


#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <netdb.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <unistd.h>

#include <openssl/crypto.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <openssl/rand.h>


#define BUF_LEN 1024


SSL *ssl;
SSL_CTX *ctx;

void sendByTLS(char* request);
void recieveFromGmail();
void readOneLine();


int
main(argc, argv)
     int argc;
     char *argv[];
{
  int ret;
  int s;
  struct hostent *servhost; 
  struct sockaddr_in server;

  char *host = "pop.gmail.com";
  char *port = "995";

  if ( argc == 2 )
  {
    //URLとポート番号を引数から得る
    host = argv[1]; 
    port = argv[2]; 
  }

  servhost = gethostbyname(host);
  if ( servhost == NULL ){
    fprintf(stderr, "[%s] から IP アドレスへの変換に失敗しました。\n", host);
    exit(1);
  }

  bzero((char *)&server, sizeof(server));
  server.sin_family = AF_INET;

  bcopy(servhost->h_addr, (char *)&server.sin_addr, servhost->h_length);

  server.sin_port = htons(atoi(port));
  
  s = socket(AF_INET, SOCK_STREAM, 0); 
  if ( s < 0 ){
    fprintf(stderr, "ソケットの生成に失敗しました。\n");
    exit(1);
  }

  if ( connect(s, (struct sockaddr*) &server, sizeof(server)) == -1 ){
    fprintf(stderr, "connect に失敗しました。\n");
    exit(1);
  }
  
  /* ------------------ ここからが SSL(それまで普通のソケット)------------------ */

  SSL_load_error_strings();                         //エラーメッセージを文字列で
  SSL_library_init();                               //SSL/TLSの初期化
  ctx = SSL_CTX_new(TLSv1_client_method());         //プロトコル選択。GmailはTLS
  if ( ctx == NULL ) { ERR_print_errors_fp(stderr); exit(1); }
  
  ssl = SSL_new(ctx);                               //SSL_CTX構造体を生成
  if ( ssl == NULL )  { ERR_print_errors_fp(stderr); exit(1); }

  ret = SSL_set_fd(ssl, s);                         //ソケットとSSLの構造体を結びつけ
  if ( ret == 0 ) { ERR_print_errors_fp(stderr); exit(1); }

  /* PRNG 初期化 (新しめのOSには要らないかもな、コレ)*/
  RAND_poll();                                      //乱数の種生成
  while ( RAND_status() == 0 ){                     //乱数の種の過不足チェック
    unsigned short rand_ret = rand() % 65536;
    RAND_seed(&rand_ret, sizeof(rand_ret));         //乱数の大きさが小さい場合追加
  }

  /* SSL で接続 */
  ret = SSL_connect(ssl);                           //サーバとハンドシェイク
  if ( ret != 1 ) { ERR_print_errors_fp(stderr); exit(1); }
  
//  readOneLine();
  recieveFromGmail();
  
  /* ------------------- リクエスト送信 ------------------- */
  // POP3的なメッセージ
  
  sendByTLS("USER ユーザー名\r\n");
  recieveFromGmail();
  
  sendByTLS("PASS パスワード名\r\n");
  recieveFromGmail();
  
  sendByTLS("STAT\r\n");
  recieveFromGmail();
  
  sendByTLS("LIST\r\n");
  recieveFromGmail();
  
//  sendByTLS("RETR 1\r\n");
//  recieveFromGmail(); //RETR途中の1230文字くらいで、下のQUITが入ってきちゃう。なぜ?
  
  sendByTLS("QUIT\r\n");
//  readOneLine();
  recieveFromGmail();
  
  
  ret = SSL_shutdown(ssl);                  //TLSのコネクションを切る
  if ( ret != 1 ){
    ERR_print_errors_fp(stderr);
    exit(1);
  }
  close(s);                                 //普通のソケットを閉じる

  SSL_free(ssl);                            //SSL_new()で確保した領域解放
  SSL_CTX_free(ctx);                        //SSL_CTX_new()で確保した領域解放
  ERR_free_strings();                       //SSL_load_error_strings()で確保した領域解放

  exit(0);
}



void sendByTLS(char* request)
{
  printf("----- クライアントからのリクエスト -----\n");
  printf(request);
  int ret = SSL_write(ssl, request, strlen(request));   //リクエスト送信 
  if ( ret < 1 ){
    ERR_print_errors_fp(stderr);
    exit(1);
  }
}



// バッファが小さいので、全部読まずに捨ててる
// 必要なのがはじめの十数バイトだからいいんだけど…
void readOneLine()
{
    printf("----- サーバからのレスポンス -----\n");
    char buf[BUF_LEN];
    int read_size = SSL_read(ssl, buf, sizeof(buf)-1);      //サーバから受信
    printf(buf);
}



// 不具合があるので直さんと。バッファが小さすぎる(32bytes以下?)と切れたりすることも
void recieveFromGmail()
{
  printf("----- サーバからのレスポンス -----\n");
  char buf[BUF_LEN];
  while (1)
  {
    memset(buf, '\0', sizeof(buf));
    int read_size = SSL_read(ssl, buf, sizeof(buf)-1);      //サーバから受信
    printf(buf);
    if ( read_size < sizeof(buf)-1) break;
  }
}



ファイルはこっち。
http://www010.upp.so-net.ne.jp/mihumi/source/pop3s-client_minimum.c

説明内容はコメントで大丈夫かな。コメントをたくさん盛る癖があるので、自分のソースを見るときは、設計書とか読む必要がないのが良いんだよね。TODOとか、不具合とか、別ファイルにしても、そんなの見るかどうかわかんないから、自分が恥ずかしい結果になるとしても、がっつりコメントを盛る。

Macでのコンパイル方法は
gcc -L/opt/local/lib -I/opt/local/include -lssl -lcrypto -arch x86_64 -o https-client https-client.c
みたいな感じで。細かいうんぬんは下のリンクにあるはず。
http://miff.blog.so-net.ne.jp/2010-12-12

結局、証明書関係は使わなかったので、すっぱり削りました。それでも、ミニマム設定にはなってないかな(乱数の発生とか)。でも、辛うじて動く状態にはなっているはずです。


nice!(1)  コメント(0) 

nice! 1

コメント 0

コメントを書く

お名前:[必須]
URL:
コメント:
画像認証:
下の画像に表示されている文字を入力してください。