kbio_htget_ssl.c

最終更新:2009/12/19

kbio_htget_ssl.c

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 */
Copyright (C) KAKU PROJECT (2009)KAKU PROJECT (2009)