kbio_htget_ssl.c
最終更新:2009/12/19
001: /* 002: * kbio_ssl.c 003: * SSL API から BIO API 経由で apr_socket を使う例 004: * HTTP/1.0 でWebサイトに接続してファイルに保存する。 005: * HTTP/1.1 にしないのは、チャンク形式に対応していないため。 006: */ 007: 008: #define USAGE "Usage: kbio_htget_ssl <uri> <out_filename>" 009: 010: #include "apr_general.h" 011: 012: #include "apr_errno.h" 013: #include "apr_strings.h" 014: #include "apr_uri.h" 015: #include "apr_tables.h" 016: 017: #include "openssl/crypto.h" 018: #include "openssl/ssl.h" 019: #include "openssl/err.h" 020: #include "openssl/rand.h" 021: #include "openssl/bio.h" 022: 023: #include "bio_apr_socket.h" 024: #include "mystab.h" 025: #include "myht_util.h" 026: 027: void trim_CRLF(char *line) { 028: int i=0; 029: int len = 0; 030: if (!line) 031: return; 032: len = strlen(line); 033: for (i=len-1; i>=0; i--) { 034: if (line[i] == '\r' || line[i] == '\n') { 035: line[i] = '\0'; 036: } else { 037: break; 038: } 039: } 040: } 041: 042: /* 概要:entity-header と CRLF の読み出し 043: * 返り値: 044: * 成功時:1 045: * 失敗時:0 最後の CRLF を見つけられなかった時 046: * メモ:読み出した entity-header は f に渡される 047: * void f (const char *line, void *ctx); 048: */ 049: 050: int BIO_read_entity_headers_CRLF_do ( 051: BIO *in /* 入力ストリーム */ 052: , void *f 053: , void *ctx 054: ) { 055: void (*func)(char *, void *) = f; 056: char line[1024]; 057: while (BIO_gets(in, line, sizeof(line)) >= 0) { 058: trim_CRLF(line); 059: if (line[0] == '\0') { 060: return 1; 061: } 062: /* 取り出した entity-header の処理 */ 063: 064: if (func) 065: (*func)(line, ctx); 066: } 067: return 0; 068: } 069: int apr_my_main ( 070: int ac 071: , char **av 072: , apr_file_t * astdin 073: , apr_file_t * astdout 074: , apr_file_t * astderr 075: , apr_pool_t * pool 076: ) { 077: 078: int error_flag = 0; 079: apr_status_t rv = APR_SUCCESS; 080: 081: 082: char *uri_str = NULL; 083: 084: apr_uri_t uri; 085: 086: char *hostname = NULL; 087: int port = 0; 088: char *out_filename = NULL; 089: 090: BIO *sbio = NULL; 091: BIO *bio = NULL; 092: BIO *bio_out = NULL; 093: 094: int ret = 1; 095: SSL_CTX *ctx = NULL; 096: #if 0 097: SSL *ssl = NULL; 098: #endif 099: 100: apr_table_t * rsp_headers = NULL; 101: 102: if (ac < 3) { 103: apr_file_printf(astderr, "%s\n", USAGE); 104: error_flag = 1; 105: goto _FINALLY_; 106: } 107: 108: uri_str = av[1]; 109: out_filename = av[2]; 110: 111: /* URI のパース */ 112: if (! my_uri_parse(pool, uri_str, &uri)) { 113: apr_file_printf(astderr, "ERROR: uri [%s] ", uri_str); 114: error_flag = 1; 115: goto _FINALLY_; 116: } 117: 118: hostname = uri.hostname; 119: port = uri.port; 120: 121: /* サーバへの接続 */ 122: sbio = bio_apr_socket_connect(hostname, port, pool); 123: if (!sbio) { 124: apr_file_printf(astderr, "ERROR: bio_apr_socket_connect [%s:%d] ", hostname, port); 125: error_flag = 1; 126: goto _FINALLY_; 127: } 128: 129: /* 出力ファイルのオープン */ 130: bio_out = BIO_new_file(out_filename, "w"); 131: if (!bio_out) { 132: apr_file_printf(astderr, "ERROR: BIO_new_file [%s] ", out_filename); 133: error_flag = 1; 134: goto _FINALLY_; 135: } 136: 137: /* SSL初期化 */ 138: SSL_load_error_strings(); 139: SSL_library_init(); 140: ctx = SSL_CTX_new(SSLv23_client_method()); 141: if ( ctx == NULL ){ 142: ERR_print_errors_fp(stderr); 143: apr_file_printf(astderr, "ERROR: SSL_CTX_new "); 144: error_flag = 1; 145: goto _FINALLY_; 146: } 147: 148: #if 0 149: ssl = SSL_new(ctx); 150: if ( ssl == NULL ){ 151: ERR_print_errors_fp(stderr); 152: apr_file_printf(astderr, "ERROR: SSL_new "); 153: error_flag = 1; 154: goto _FINALLY_; 155: } 156: 157: SSL_set_bio(ssl, bio, bio); 158: #endif 159: 160: /* BIO 構造体に SSL コンテクストを保持し、ソケット BIO でチェイン化する。 */ 161: bio = BIO_push(BIO_new_ssl(ctx, 1), sbio); 162: if (!bio) { 163: apr_file_printf(astderr, "ERROR: BIO_push "); 164: error_flag = 1; 165: goto _FINALLY_; 166: } 167: 168: /* PRNG 初期化 */ 169: RAND_poll(); 170: while ( RAND_status() == 0 ){ 171: unsigned short rand_ret = rand() % 65536; 172: RAND_seed(&rand_ret, sizeof(rand_ret)); 173: } 174: 175: /* SSL で接続 */ 176: #if 0 177: ret = SSL_connect(ssl); 178: #endif 179: ret = BIO_do_connect(bio); 180: if (ret != 1) { 181: ERR_print_errors_fp(stderr); 182: apr_file_printf(astderr, "ERROR: SSL_connect "); 183: error_flag = 1; 184: goto _FINALLY_; 185: } 186: 187: /* リクエストの送信 */ 188: { 189: int nbytes = 0; 190: char *req = apr_pstrcat(pool, "GET ", uri.path, " HTTP/1.0\r\n", NULL); 191: if ((!strcmp(uri.scheme, "http") && uri.port == 80) || 192: (!strcmp(uri.scheme, "https") && uri.port == 443) ) { 193: req = apr_pstrcat(pool, req, "Host: ", uri.hostname, "\r\n", NULL); 194: } else { 195: req = apr_pstrcat(pool, req, "Host: ", uri.hostname, ":", uri.port_str, 196: "\r\n", NULL); 197: } 198: req = apr_pstrcat(pool, req, "Accept: */*\r\n", NULL); 199: req = apr_pstrcat(pool, req, "User-Agent: khtget/0.1\r\n\r\n", NULL); 200: 201: nbytes = BIO_write(bio, req, strlen(req)); 202: if (nbytes <= 0) { 203: apr_file_printf(astderr, "ERROR: BIO_write "); 204: error_flag = 1; 205: goto _FINALLY_; 206: } 207: } 208: 209: /* バッファリングフィルタ */ 210: { 211: bio = BIO_push(BIO_new(BIO_f_buffer()), bio); 212: if (!bio) { 213: apr_file_printf(astderr, "ERROR: BIO_push "); 214: error_flag = 1; 215: goto _FINALLY_; 216: } 217: } 218: 219: /* レスポンスラインの受信 */ 220: { 221: char rsp_line[1024]; 222: char *rsp_version=NULL; 223: char *status_str=NULL; 224: char *reason_phrase=NULL; 225: int ret = 0; 226: 227: if (BIO_gets(bio, rsp_line, sizeof(rsp_line)) < 0) { 228: error_flag = 1; 229: goto _FINALLY_; 230: } 231: trim_CRLF(rsp_line); 232: ret = read_rsp_line(rsp_line, 233: &rsp_version, &status_str, &reason_phrase, ' '); 234: if (ret < 3) { 235: apr_file_printf(astderr, "ERROR: read_rsp_line "); 236: error_flag = 1; 237: goto _FINALLY_; 238: } 239: apr_file_printf(astdout, "[%s] [%s] [%s]\n", 240: rsp_version, status_str, reason_phrase); 241: } 242: 243: /* レスポンスヘッダの受信 */ 244: rsp_headers = apr_table_make(pool, 10); 245: { 246: int ret = BIO_read_entity_headers_CRLF_do (bio, set_entity_header_to_tab, 247: rsp_headers); 248: if (! ret) { 249: apr_file_printf(astderr, "ERROR: BIO_read_entity_headers_CRLF_do "); 250: error_flag = 1; 251: goto _FINALLY_; 252: } 253: } 254: 255: /* レスポンスヘッダの表示 */ 256: apr_table_do(disp_tab, astdout, rsp_headers, NULL); 257: 258: /* レスポンスボディの受信〜ファイル出力 */ 259: { 260: char buf[1024]; 261: while (1) { 262: int nbytes = BIO_read(bio, buf, sizeof(buf)); 263: if (nbytes <= 0) { 264: break; 265: } 266: nbytes = BIO_write(bio_out, buf, nbytes); 267: if (nbytes < 0) { 268: break; 269: } 270: } 271: } 272: 273: _FINALLY_: 274: 275: #if 0 276: if (ssl) { 277: ret = SSL_shutdown(ssl); 278: if ( ret != 1 ){ 279: ERR_print_errors_fp(stderr); 280: error_flag = 1; 281: } 282: 283: SSL_free(ssl); 284: ssl = NULL; 285: } 286: #endif 287: 288: if (ctx) { 289: SSL_CTX_free(ctx); 290: ctx = NULL; 291: } 292: ERR_free_strings(); 293: 294: if (bio) { 295: BIO_free_all(bio); 296: bio = NULL; 297: } 298: if (bio_out) { 299: BIO_free_all(bio_out); 300: bio_out = NULL; 301: } 302: 303: if (rv != APR_SUCCESS) { 304: char error_buf[80]; 305: apr_file_printf(astderr, "ERROR: %s\n", 306: apr_strerror(rv, error_buf, sizeof(error_buf))); 307: } 308: 309: if (error_flag) { 310: apr_file_printf(astderr, "failed!\n"); 311: return 1; /* 異常終了 */ 312: } 313: 314: apr_file_printf(astdout, "\ndone.\n"); 315: 316: return 0; /* 正常終了 */ 317: 318: } /* end of main */
KAKU PROJECT (2009) |