diff options
author | Igor Sysoev <igor@sysoev.ru> | 2006-05-29 17:28:12 +0000 |
---|---|---|
committer | Igor Sysoev <igor@sysoev.ru> | 2006-05-29 17:28:12 +0000 |
commit | afd7ec53572d817d155be5b8a5b6aab7ebbdcb5a (patch) | |
tree | 5543319d12f175a80fbdc6b10e72788d91a0a623 /src/mysql/ngx_mysql.c | |
parent | a33fd634b0606f068ad39edd8374c035d353c590 (diff) | |
download | nginx-afd7ec53572d817d155be5b8a5b6aab7ebbdcb5a.tar.gz nginx-afd7ec53572d817d155be5b8a5b6aab7ebbdcb5a.zip |
nginx-0.3.48-RELEASE importrelease-0.3.48
*) Change: now the ngx_http_charset_module works for subrequests, if
the response has no "Content-Type" header line.
*) Bugfix: if the "proxy_pass" directive has no URI part, then the
"proxy_redirect default" directive add the unnecessary slash in
start of the rewritten redirect.
*) Bugfix: the internal redirect always transform client's HTTP method
to GET, now the transformation is made for the "X-Accel-Redirect"
redirects only and if the method is not HEAD; the bug had appeared
in 0.3.42.
*) Bugfix: the ngx_http_perl_module could not be built, if the perl was
built with the threads support; the bug had appeared in 0.3.46.
Diffstat (limited to 'src/mysql/ngx_mysql.c')
-rw-r--r-- | src/mysql/ngx_mysql.c | 386 |
1 files changed, 360 insertions, 26 deletions
diff --git a/src/mysql/ngx_mysql.c b/src/mysql/ngx_mysql.c index 1e008b5e4..daabcd855 100644 --- a/src/mysql/ngx_mysql.c +++ b/src/mysql/ngx_mysql.c @@ -4,13 +4,100 @@ */ +/* the library supports the subset of the MySQL 4.1+ protocol (version 10) */ + + #include <ngx_config.h> #include <ngx_core.h> #include <ngx_event.h> +#include <ngx_event_connect.h> #include <ngx_mysql.h> +#if (NGX_HAVE_OPENSSL_SHA1_H) +#include <openssl/sha.h> +#else +#include <sha.h> +#endif -/* the library supports the subset of the MySQL 4.1+ protocol (version 10) */ + +#define NGX_MYSQL_LONG_PASSWORD 0x0001 +#define NGX_MYSQL_CONNECT_WITH_DB 0x0008 +#define NGX_MYSQL_PROTOCOL_41 0x0200 +#define NGX_MYSQL_SECURE_CONNECTION 0x8000 + + +#define NGX_MYSQL_CMD_QUERY 3 + + +typedef struct { + u_char pktlen[3]; + u_char pktn; + + u_char protocol; + u_char version[1]; /* NULL-terminated string */ +} ngx_mysql_greeting1_pkt_t; + + +typedef struct { + u_char thread[4]; + u_char salt1[9]; + u_char capacity[2]; + u_char charset; + u_char status[2]; + u_char zero[13]; + u_char salt2[13]; +} ngx_mysql_greeting2_pkt_t; + + +typedef struct { + u_char pktlen[3]; + u_char pktn; + + u_char capacity[4]; + u_char max_packet[4]; + u_char charset; + u_char zero[23]; + u_char login[1]; /* NULL-terminated string */ + + /* + * u_char passwd_len; 0 if no password + * u_char passwd[20]; + * + * u_char database[1]; NULL-terminated string + */ + +} ngx_mysql_auth_pkt_t; + + +typedef struct { + u_char pktlen[3]; + u_char pktn; + u_char fields; +} ngx_mysql_response_pkt_t; + + +typedef struct { + u_char pktlen[3]; + u_char pktn; + u_char err; + u_char code[2]; + u_char message[1]; /* string */ +} ngx_mysql_error_pkt_t; + + +typedef struct { + u_char pktlen[3]; + u_char pktn; + u_char command; + u_char arg[1]; /* string */ +} ngx_mysql_command_pkt_t; + + +static void ngx_mysql_read_server_greeting(ngx_event_t *rev); +static void ngx_mysql_empty_handler(ngx_event_t *wev); +static void ngx_mysql_read_auth_result(ngx_event_t *rev); +static void ngx_mysql_read_query_result(ngx_event_t *rev); +static void ngx_mysql_close(ngx_mysql_t *m, ngx_int_t rc); ngx_int_t @@ -32,11 +119,12 @@ ngx_mysql_connect(ngx_mysql_t *m) return rc; } + m->peer.connection->data = m; + m->peer.connection->read->handler = ngx_mysql_read_server_greeting; - m->peer.connection->write->handler = ngx_mysql_emtpy_handler; + m->peer.connection->write->handler = ngx_mysql_empty_handler; ngx_add_timer(m->peer.connection->read, /* STUB */ 5000); - ngx_add_timer(m->peer.connection->write, /* STUB */ 5000); return NGX_OK; } @@ -45,10 +133,17 @@ ngx_mysql_connect(ngx_mysql_t *m) static void ngx_mysql_read_server_greeting(ngx_event_t *rev) { - size_t len; - u_char *p, *t; - ngx_mysql_t *m; - ngx_connection_t *c; + size_t len; + u_char *p; + ssize_t n; + ngx_uint_t i, capacity; + ngx_mysql_t *m; + ngx_connection_t *c; + ngx_mysql_greeting1_pkt_t *gr1; + ngx_mysql_greeting2_pkt_t *gr2; + ngx_mysql_auth_pkt_t *auth; + SHA_CTX sha; + u_char hash1[20], hash2[20]; c = rev->data; m = c->data; @@ -56,16 +151,16 @@ ngx_mysql_read_server_greeting(ngx_event_t *rev) if (rev->timedout) { ngx_log_error(NGX_LOG_ERR, rev->log, NGX_ETIMEDOUT, "mysql server %V timed out", - &ctx->peer.peers->peer[0].name); + &m->peer.peers->peer[0].name); ngx_mysql_close(m, NGX_ERROR); return; } if (m->buf == NULL) { - m->peer.log->action = "reading to mysql server greeting"; + m->peer.log->action = "reading mysql server greeting"; - m->buf = ngx_create_temp(m->pool, /* STUB */ 1024); + m->buf = ngx_create_temp_buf(m->pool, /* STUB */ 1024); if (m->buf == NULL) { ngx_mysql_close(m, NGX_ERROR); return; @@ -83,43 +178,282 @@ ngx_mysql_read_server_greeting(ngx_event_t *rev) return; } - p = m->buf->pos; + gr1 = (ngx_mysql_greeting1_pkt_t *) m->buf->pos; - if (ngx_m24toh(p) > n - 4) { + if (ngx_m24toh(gr1->pktlen) > n - 4) { ngx_log_error(NGX_LOG_ERR, rev->log, 0, "mysql server %V sent incomplete greeting packet", - &ctx->peer.peers->peer[0].name); + &m->peer.peers->peer[0].name); ngx_mysql_close(m, NGX_ERROR); return; } - if (p[4]) < 10) { + if (gr1->protocol < 10) { ngx_log_error(NGX_LOG_ERR, rev->log, 0, "mysql server %V sent unsupported protocol version %ud", - &ctx->peer.peers->peer[0].name, p[4]); + &m->peer.peers->peer[0].name, gr1->protocol); ngx_mysql_close(m, NGX_ERROR); return; } - len = ngx_strlen(&p[5]); - t = p + 5 + len + 1; + gr2 = (ngx_mysql_greeting2_pkt_t *) + (gr1->version + ngx_strlen(gr1->version) + 1); - capacity = ngx_m16toh((&t[4 + 9])); + capacity = ngx_m16toh(gr2->capacity); ngx_log_debug8(NGX_LOG_DEBUG_MYSQL, rev->log, 0, - "mysql version: %ud, \"%s\", thread: %ud, salt: \"%s\", ", + "mysql version: %ud, \"%s\", thread: %ud, salt: \"%s\", " "capacity: %Xd, charset: %ud, status: %ud, salt rest \"%s\"", - p[4], &p[5], ngx_m32toh(t), &t[4], - capacity, t[4 + 9 + 2], - ngx_m16toh((&t[4 + 9 + 2 + 1])), - t[4 + 9 + 2 + 1 + 2 + 13]); + gr1->protocol, gr1->version, ngx_m32toh(gr2->thread), + gr2->salt1, capacity, gr2->charset, + ngx_m16toh(gr2->status), &gr2->salt2); + + capacity = NGX_MYSQL_LONG_PASSWORD + | NGX_MYSQL_CONNECT_WITH_DB + | NGX_MYSQL_PROTOCOL_41 + | NGX_MYSQL_SECURE_CONNECTION; + + len = 4 + 4 + 4 + 1 + 23 + m->login->len + 1 + 1 + m->database->len + 1; + + if (m->passwd->len) { + len += 20; + } + + auth = ngx_palloc(m->pool, len); + if (auth == NULL) { + ngx_mysql_close(m, NGX_ERROR); + return; + } + + ngx_htom24(auth->pktlen, len - 4); + auth->pktn = (u_char) (gr1->pktn + 1); + + ngx_htom32(auth->capacity, capacity); + ngx_htom32(auth->max_packet, 0x01000000); /* max packet size 2^24 */ + ngx_memzero(auth->zero, 24); + auth->charset = gr2->charset; + + p = ngx_copy(auth->login, m->login->data, m->login->len); + *p++ = '\0'; + + if (m->passwd->len) { + + *p++ = (u_char) 20; + + SHA1_Init(&sha); + SHA1_Update(&sha, m->passwd->data, m->passwd->len); + SHA1_Final(hash1, &sha); + + SHA1_Init(&sha); + SHA1_Update(&sha, hash1, 20); + SHA1_Final(hash2, &sha); + + SHA1_Init(&sha); + SHA1_Update(&sha, gr2->salt1, 8); + SHA1_Update(&sha, gr2->salt2, 12); + SHA1_Update(&sha, hash2, 20); + SHA1_Final(hash2, &sha); + + for (i = 0; i < 20; i++) { + *p++ = (u_char) (hash1[i] ^ hash2[i]); + } + + } else { + *p++ = '\0'; + } + + p = ngx_copy(p, m->database->data, m->database->len); + *p = '\0'; + + + n = ngx_send(m->peer.connection, (void *) auth, len); + + if (n < (ssize_t) len) { + ngx_log_error(NGX_LOG_ERR, rev->log, 0, + "the incomplete packet was sent to mysql server %V", + &m->peer.peers->peer[0].name); + + ngx_mysql_close(m, NGX_ERROR); + return; + } + + m->peer.connection->read->handler = ngx_mysql_read_auth_result; + + ngx_add_timer(m->peer.connection->read, /* STUB */ 5000); +} + + +static void +ngx_mysql_empty_handler(ngx_event_t *wev) +{ + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, wev->log, 0, "mysql empty handler"); + + return; +} + + +static void +ngx_mysql_read_auth_result(ngx_event_t *rev) +{ + ssize_t n, len; + ngx_str_t msg; + ngx_mysql_t *m; + ngx_connection_t *c; + ngx_mysql_error_pkt_t *epkt; + ngx_mysql_response_pkt_t *pkt; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0, "mysql read auth"); + + c = rev->data; + m = c->data; + + m->peer.log->action = "reading mysql auth result"; + + n = ngx_recv(m->peer.connection, m->buf->pos, /* STUB */ 1024); + + if (n == NGX_AGAIN) { + return; + } + + if (n < 5) { + ngx_mysql_close(m, NGX_ERROR); + return; + } + + pkt = (ngx_mysql_response_pkt_t *) m->buf->pos; + + len = ngx_m24toh(pkt->pktlen); + + if (len > n - 4) { + ngx_log_error(NGX_LOG_ERR, rev->log, 0, + "mysql server %V sent incomplete response packet", + &m->peer.peers->peer[0].name); + + ngx_mysql_close(m, NGX_ERROR); + return; + } + + if (pkt->fields == 0) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0, "mysql auth OK"); + + m->state = NGX_OK; + m->pktn = 0; + + m->handler(m); + + return; + } + + epkt = (ngx_mysql_error_pkt_t *) pkt; + + msg.len = (u_char *) epkt + 4 + len - epkt->message; + msg.data = epkt->message; + + ngx_log_error(NGX_LOG_ERR, rev->log, 0, + "mysql server %V sent error (%ud): \"%V\"", + &m->peer.peers->peer[0].name, ngx_m16toh(epkt->code), &msg); + + ngx_mysql_close(m, NGX_ERROR); +} + + +ngx_int_t +ngx_mysql_query(ngx_mysql_t *m) +{ + ssize_t n; + ngx_mysql_command_pkt_t *pkt; + + pkt = (ngx_mysql_command_pkt_t *) m->query.data; + + ngx_htom24(pkt->pktlen, m->query.len - 4); + pkt->pktn = (u_char) m->pktn++; + pkt->command = NGX_MYSQL_CMD_QUERY; + + n = ngx_send(m->peer.connection, m->query.data, m->query.len); + + if (n < (ssize_t) m->query.len) { + ngx_log_error(NGX_LOG_ERR, m->peer.log, 0, + "the incomplete packet was sent to mysql server %V", + &m->peer.peers->peer[0].name); + + ngx_mysql_close(m, NGX_ERROR); + return NGX_OK; + } + + m->peer.connection->read->handler = ngx_mysql_read_query_result; + + ngx_add_timer(m->peer.connection->read, /* STUB */ 5000); + + /* STUB handle event */ + + return NGX_OK; +} + + +static void +ngx_mysql_read_query_result(ngx_event_t *rev) +{ + ssize_t n, len; + ngx_str_t msg; + ngx_mysql_t *m; + ngx_connection_t *c; + ngx_mysql_error_pkt_t *epkt; + ngx_mysql_response_pkt_t *pkt; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0, "mysql read query result"); + + c = rev->data; + m = c->data; + + m->peer.log->action = "reading mysql read query result"; + + n = ngx_recv(m->peer.connection, m->buf->pos, /* STUB */ 1024); + + if (n == NGX_AGAIN) { + return; + } + + if (n < 5) { + ngx_mysql_close(m, NGX_ERROR); + return; + } + + pkt = (ngx_mysql_response_pkt_t *) m->buf->pos; + + len = ngx_m24toh(pkt->pktlen); + + if (len > n - 4) { + ngx_log_error(NGX_LOG_ERR, rev->log, 0, + "mysql server %V sent incomplete response packet", + &m->peer.peers->peer[0].name); + + ngx_mysql_close(m, NGX_ERROR); + return; + } + + if (pkt->fields != 0xff) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0, "mysql query OK"); + + m->state = NGX_OK; + m->pktn = pkt->pktn; + + m->handler(m); + + return; + } + + epkt = (ngx_mysql_error_pkt_t *) pkt; + + msg.len = (u_char *) epkt + 4 + len - epkt->message; + msg.data = epkt->message; - capacity &= NGX_MYSQL_LONG_PASSWORD - | NGX_MYSQL_CONNECT_WITH_DB - | NGX_MYSQL_PROTOCOL_41; + ngx_log_error(NGX_LOG_ERR, rev->log, 0, + "mysql server %V sent error (%ud): \"%V\"", + &m->peer.peers->peer[0].name, ngx_m16toh(epkt->code), &msg); + ngx_mysql_close(m, NGX_ERROR); } |