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) |