/* * bio_apr_socket.c * BIO API 用 apr_socket_t 版ソケット * OpenSSL の入出力フレームワークの BIO で apr_socket_t を対応させる試み。 * ・read / write のみ使用可能 * ・バッファリングなし。従って、gets は効率が悪い。 * * 入出力ストリームのフレームワークとして、OpenSSL の BIO と APR の * Buckets Brigade と複数の方式があって悩ましい。 * 現時点では、OpenSSL の SSL API を使いたいので、BIO ということになるのだが、 * 慣れてきたので APR Buckets Brigade で統一したいところだ。 * * それにしても、C 言語を使ったストリームの抽象化ってわかりにくい。 * こういうのはオブジェクト指向言語の方が向いてる。 * Java の java.io.*Stream は実に使いやすかった。 * */ #ifndef APR_NETWORK_IO_H #include "apr_network_io.h" #endif #ifndef HEADER_BIO_H #include "openssl/bio.h" #endif #include "bio_apr_socket.h" typedef struct bio_apr_socket_ctx_st { apr_socket_t *sock; int seen_eof; } bio_apr_socket_ctx; int bio_apr_socket_create( BIO *bio ) { bio->shutdown = 1; bio->init = 1; bio->num = -1; bio->ptr = NULL; return 1; } int bio_apr_socket_destroy( BIO *bio ) { if (!bio) { return 0; } return 1; } int bio_apr_socket_write( BIO *bio , const char *out_buf , int out_len ) { apr_status_t rv=APR_SUCCESS; bio_apr_socket_ctx *ctx= (bio_apr_socket_ctx*)(bio->ptr); apr_socket_t *sock = ctx->sock; apr_size_t len = out_len; if (!sock || !out_buf || out_len <= 0) { return -1; } rv = apr_socket_send(sock, out_buf, &len); if (rv != APR_SUCCESS) { ctx->seen_eof = 1; return -1; } return len; } int bio_apr_socket_read ( BIO *bio , char *in_buf , int in_len ) { apr_status_t rv=APR_SUCCESS; bio_apr_socket_ctx *ctx= (bio_apr_socket_ctx*)(bio->ptr); apr_socket_t *sock = ctx->sock; apr_size_t len = in_len; if (!sock || !in_buf || in_len <= 0) { return -1; } rv = apr_socket_recv(sock, in_buf, &len); if (rv != APR_SUCCESS) { ctx->seen_eof = 1; return -1; } return len; } /* LF で終わる文字列を取得する。 * ・CRLF もしくは LF で終わる場合はヌル文字で終わる文字列を buf に保存。 * ・size-2 の長さ以内で CRLF もしくは LF を見つけられなかった場合は * バッファの size-1 はヌル文字にする。エラーにすべきかもしれない。 * * ・内部のバッファを実装するのが面倒なので、何度も1バイト単位で BIO_read を * 呼ぶ。…従って効率が悪い。 */ int bio_apr_socket_gets( BIO *bio , char *buf , int size ) { int len = 0; if (!buf || size<3) { return -1; } while (len < size-2) { char b[1]; int nbytes = BIO_read(bio, b, 1); if (nbytes < 1 ) { return -1; } if (b[0] == '\n') { buf[len] = '\0'; if (len>0 && buf[len-1] == '\r') { /* CRLF の時 */ buf[len-1] = '\0'; len--; } break; } else { buf[len] = b[0]; } len++; } /* 与えられたバッファサイズを過ぎても CRLF または LF を見つけられなかった時 * とりあえずこの実装では最後にヌル文字を入れている。 * エラーとすべきかもしれない。 */ if (len == size-2) { buf[size-1] = '\0'; } /* if (len == 0) { return -1; } */ return len; } int bio_apr_socket_puts( BIO *bio , const char *str ) { apr_status_t rv=APR_SUCCESS; bio_apr_socket_ctx *ctx= (bio_apr_socket_ctx*)(bio->ptr); apr_socket_t *sock = ctx->sock; apr_size_t len = 0; if (!sock || !str) { return -1; } len = strlen(str); rv = apr_socket_send(sock, str, &len); if (rv != APR_SUCCESS) { ctx->seen_eof = 1; return -1; } return len; } /* この関数は自信がない…。 * */ long bio_apr_socket_ctrl( BIO *bio , int cmd , long num , void *ptr ) { long ret = 1; bio_apr_socket_ctx *ctx= (bio_apr_socket_ctx*)(bio->ptr); switch (cmd) { case BIO_CTRL_RESET:/* opt - rewind/zero etc */ ; break; case BIO_CTRL_EOF:/* opt - are we at the eof */ if(ctx->seen_eof) { ret = 1L; } else { ret = 0L; } break; case BIO_C_SET_BUF_MEM_EOF_RETURN:/*return end of input value*/ ret = 1L; break; case BIO_CTRL_INFO:/* opt - extra tit-bits */ ret = 0L; break; case BIO_CTRL_GET_CLOSE:/* man - set the 'close' on free */ ret = (long)bio->shutdown; break; case BIO_CTRL_SET_CLOSE:/* man - set the 'close' on free */ bio->shutdown = (int)num; break; case BIO_CTRL_WPENDING: /* opt - number of bytes still to write */ ret = 0L; break; case BIO_CTRL_PENDING:/* opt - is their more data buffered */ ret = 0L; break; case BIO_CTRL_FLUSH:/* opt - 'flush' buffered output */ ret = 1L; break; case BIO_CTRL_DUP:/* man - extra stuff for 'duped' BIO */ ret = 1L; break; /* N/A */ case BIO_C_SET_BUF_MEM: case BIO_C_GET_BUF_MEM_PTR: /* we don't care */ case BIO_CTRL_PUSH: case BIO_CTRL_POP: default: ret = 0; break; } return ret; } BIO_METHOD bio_apr_socket_method = { BIO_TYPE_MEM, "APR Socket", bio_apr_socket_write, bio_apr_socket_read, bio_apr_socket_puts, /* puts will be not called */ bio_apr_socket_gets, /* gets will be not called */ bio_apr_socket_ctrl, bio_apr_socket_create, bio_apr_socket_destroy, #ifdef OPENSSL_VERSION_NUMBER NULL /* sslc does not have the callback_ctrl field */ #endif }; /* * ソケット接続関数 */ static apr_socket_t *my_connect ( char *hostname , int port , apr_pool_t * pool ) { apr_socket_t *s = NULL; if (! pool || ! hostname || port<=0) { return NULL; } else { apr_status_t rv = APR_SUCCESS; apr_sockaddr_t *sa=NULL; rv = apr_sockaddr_info_get(&sa, hostname, APR_UNSPEC, port, 0, pool); if (APR_SUCCESS != rv) { return NULL; } for (; sa!=NULL; sa=sa->next) { rv = apr_socket_create(&s, sa->family, SOCK_STREAM, APR_PROTO_TCP, pool); if (APR_SUCCESS != rv) { continue; } rv = apr_socket_connect(s, sa); if (APR_SUCCESS == rv) { break; } } if (!sa) { return NULL; } } return s; } /* * オープン済みのソケットを使って BIO オブジェクトを生成する関数 * 返り値: * 成功時:ソケットを保持した BIO オブジェクト * 失敗時:ヌル */ BIO *bio_apr_socket_new ( apr_socket_t *sock /* BIO オブジェクトに保持させるソケット */ , apr_pool_t *pool ) { BIO *bio = NULL; if (!sock) { return NULL; } if(sock) { bio_apr_socket_ctx *ctx = (bio_apr_socket_ctx*)apr_palloc(pool, sizeof(bio_apr_socket_ctx)); memset(ctx, 0, sizeof(bio_apr_socket_ctx)); ctx->sock = sock; ctx->seen_eof = 0; bio = BIO_new(&bio_apr_socket_method); bio->ptr = ctx; } return bio; } /* * ソケット接続し、BIO オブジェクトを生成する関数 * 返り値: * 成功時:ソケットを保持した BIO オブジェクト * 失敗時:ヌル * メモ: * ソケットオプションはデフォルト設定。 */ BIO *bio_apr_socket_connect( char *hostname /* 接続先ホスト名 */ , int port /* 接続先ポート番号 */ , apr_pool_t * pool ) { apr_socket_t *sock = my_connect (hostname, port, pool); if (!sock) { return NULL; } return bio_apr_socket_new(sock, pool); }