Mercurial > hg > nginx
comparison src/mysql/ngx_mysql.c @ 653:7cbef16c71a1 release-0.3.48
nginx-0.3.48-RELEASE import
*) 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.
author | Igor Sysoev <igor@sysoev.ru> |
---|---|
date | Mon, 29 May 2006 17:28:12 +0000 |
parents | 4946078f0a79 |
children | 4d68c486fcb0 |
comparison
equal
deleted
inserted
replaced
652:d01fc553611d | 653:7cbef16c71a1 |
---|---|
2 /* | 2 /* |
3 * Copyright (C) Igor Sysoev | 3 * Copyright (C) Igor Sysoev |
4 */ | 4 */ |
5 | 5 |
6 | 6 |
7 /* the library supports the subset of the MySQL 4.1+ protocol (version 10) */ | |
8 | |
9 | |
7 #include <ngx_config.h> | 10 #include <ngx_config.h> |
8 #include <ngx_core.h> | 11 #include <ngx_core.h> |
9 #include <ngx_event.h> | 12 #include <ngx_event.h> |
13 #include <ngx_event_connect.h> | |
10 #include <ngx_mysql.h> | 14 #include <ngx_mysql.h> |
11 | 15 |
12 | 16 #if (NGX_HAVE_OPENSSL_SHA1_H) |
13 /* the library supports the subset of the MySQL 4.1+ protocol (version 10) */ | 17 #include <openssl/sha.h> |
18 #else | |
19 #include <sha.h> | |
20 #endif | |
21 | |
22 | |
23 #define NGX_MYSQL_LONG_PASSWORD 0x0001 | |
24 #define NGX_MYSQL_CONNECT_WITH_DB 0x0008 | |
25 #define NGX_MYSQL_PROTOCOL_41 0x0200 | |
26 #define NGX_MYSQL_SECURE_CONNECTION 0x8000 | |
27 | |
28 | |
29 #define NGX_MYSQL_CMD_QUERY 3 | |
30 | |
31 | |
32 typedef struct { | |
33 u_char pktlen[3]; | |
34 u_char pktn; | |
35 | |
36 u_char protocol; | |
37 u_char version[1]; /* NULL-terminated string */ | |
38 } ngx_mysql_greeting1_pkt_t; | |
39 | |
40 | |
41 typedef struct { | |
42 u_char thread[4]; | |
43 u_char salt1[9]; | |
44 u_char capacity[2]; | |
45 u_char charset; | |
46 u_char status[2]; | |
47 u_char zero[13]; | |
48 u_char salt2[13]; | |
49 } ngx_mysql_greeting2_pkt_t; | |
50 | |
51 | |
52 typedef struct { | |
53 u_char pktlen[3]; | |
54 u_char pktn; | |
55 | |
56 u_char capacity[4]; | |
57 u_char max_packet[4]; | |
58 u_char charset; | |
59 u_char zero[23]; | |
60 u_char login[1]; /* NULL-terminated string */ | |
61 | |
62 /* | |
63 * u_char passwd_len; 0 if no password | |
64 * u_char passwd[20]; | |
65 * | |
66 * u_char database[1]; NULL-terminated string | |
67 */ | |
68 | |
69 } ngx_mysql_auth_pkt_t; | |
70 | |
71 | |
72 typedef struct { | |
73 u_char pktlen[3]; | |
74 u_char pktn; | |
75 u_char fields; | |
76 } ngx_mysql_response_pkt_t; | |
77 | |
78 | |
79 typedef struct { | |
80 u_char pktlen[3]; | |
81 u_char pktn; | |
82 u_char err; | |
83 u_char code[2]; | |
84 u_char message[1]; /* string */ | |
85 } ngx_mysql_error_pkt_t; | |
86 | |
87 | |
88 typedef struct { | |
89 u_char pktlen[3]; | |
90 u_char pktn; | |
91 u_char command; | |
92 u_char arg[1]; /* string */ | |
93 } ngx_mysql_command_pkt_t; | |
94 | |
95 | |
96 static void ngx_mysql_read_server_greeting(ngx_event_t *rev); | |
97 static void ngx_mysql_empty_handler(ngx_event_t *wev); | |
98 static void ngx_mysql_read_auth_result(ngx_event_t *rev); | |
99 static void ngx_mysql_read_query_result(ngx_event_t *rev); | |
100 static void ngx_mysql_close(ngx_mysql_t *m, ngx_int_t rc); | |
14 | 101 |
15 | 102 |
16 ngx_int_t | 103 ngx_int_t |
17 ngx_mysql_connect(ngx_mysql_t *m) | 104 ngx_mysql_connect(ngx_mysql_t *m) |
18 { | 105 { |
30 | 117 |
31 if (rc == NGX_ERROR || rc == NGX_BUSY || rc == NGX_DECLINED) { | 118 if (rc == NGX_ERROR || rc == NGX_BUSY || rc == NGX_DECLINED) { |
32 return rc; | 119 return rc; |
33 } | 120 } |
34 | 121 |
122 m->peer.connection->data = m; | |
123 | |
35 m->peer.connection->read->handler = ngx_mysql_read_server_greeting; | 124 m->peer.connection->read->handler = ngx_mysql_read_server_greeting; |
36 m->peer.connection->write->handler = ngx_mysql_emtpy_handler; | 125 m->peer.connection->write->handler = ngx_mysql_empty_handler; |
37 | 126 |
38 ngx_add_timer(m->peer.connection->read, /* STUB */ 5000); | 127 ngx_add_timer(m->peer.connection->read, /* STUB */ 5000); |
39 ngx_add_timer(m->peer.connection->write, /* STUB */ 5000); | |
40 | 128 |
41 return NGX_OK; | 129 return NGX_OK; |
42 } | 130 } |
43 | 131 |
44 | 132 |
45 static void | 133 static void |
46 ngx_mysql_read_server_greeting(ngx_event_t *rev) | 134 ngx_mysql_read_server_greeting(ngx_event_t *rev) |
47 { | 135 { |
48 size_t len; | 136 size_t len; |
49 u_char *p, *t; | 137 u_char *p; |
50 ngx_mysql_t *m; | 138 ssize_t n; |
51 ngx_connection_t *c; | 139 ngx_uint_t i, capacity; |
140 ngx_mysql_t *m; | |
141 ngx_connection_t *c; | |
142 ngx_mysql_greeting1_pkt_t *gr1; | |
143 ngx_mysql_greeting2_pkt_t *gr2; | |
144 ngx_mysql_auth_pkt_t *auth; | |
145 SHA_CTX sha; | |
146 u_char hash1[20], hash2[20]; | |
52 | 147 |
53 c = rev->data; | 148 c = rev->data; |
54 m = c->data; | 149 m = c->data; |
55 | 150 |
56 if (rev->timedout) { | 151 if (rev->timedout) { |
57 ngx_log_error(NGX_LOG_ERR, rev->log, NGX_ETIMEDOUT, | 152 ngx_log_error(NGX_LOG_ERR, rev->log, NGX_ETIMEDOUT, |
58 "mysql server %V timed out", | 153 "mysql server %V timed out", |
59 &ctx->peer.peers->peer[0].name); | 154 &m->peer.peers->peer[0].name); |
60 | 155 |
61 ngx_mysql_close(m, NGX_ERROR); | 156 ngx_mysql_close(m, NGX_ERROR); |
62 return; | 157 return; |
63 } | 158 } |
64 | 159 |
65 if (m->buf == NULL) { | 160 if (m->buf == NULL) { |
66 m->peer.log->action = "reading to mysql server greeting"; | 161 m->peer.log->action = "reading mysql server greeting"; |
67 | 162 |
68 m->buf = ngx_create_temp(m->pool, /* STUB */ 1024); | 163 m->buf = ngx_create_temp_buf(m->pool, /* STUB */ 1024); |
69 if (m->buf == NULL) { | 164 if (m->buf == NULL) { |
70 ngx_mysql_close(m, NGX_ERROR); | 165 ngx_mysql_close(m, NGX_ERROR); |
71 return; | 166 return; |
72 } | 167 } |
73 } | 168 } |
81 if (n < 5) { | 176 if (n < 5) { |
82 ngx_mysql_close(m, NGX_ERROR); | 177 ngx_mysql_close(m, NGX_ERROR); |
83 return; | 178 return; |
84 } | 179 } |
85 | 180 |
86 p = m->buf->pos; | 181 gr1 = (ngx_mysql_greeting1_pkt_t *) m->buf->pos; |
87 | 182 |
88 if (ngx_m24toh(p) > n - 4) { | 183 if (ngx_m24toh(gr1->pktlen) > n - 4) { |
89 ngx_log_error(NGX_LOG_ERR, rev->log, 0, | 184 ngx_log_error(NGX_LOG_ERR, rev->log, 0, |
90 "mysql server %V sent incomplete greeting packet", | 185 "mysql server %V sent incomplete greeting packet", |
91 &ctx->peer.peers->peer[0].name); | 186 &m->peer.peers->peer[0].name); |
92 | 187 |
93 ngx_mysql_close(m, NGX_ERROR); | 188 ngx_mysql_close(m, NGX_ERROR); |
94 return; | 189 return; |
95 } | 190 } |
96 | 191 |
97 if (p[4]) < 10) { | 192 if (gr1->protocol < 10) { |
98 ngx_log_error(NGX_LOG_ERR, rev->log, 0, | 193 ngx_log_error(NGX_LOG_ERR, rev->log, 0, |
99 "mysql server %V sent unsupported protocol version %ud", | 194 "mysql server %V sent unsupported protocol version %ud", |
100 &ctx->peer.peers->peer[0].name, p[4]); | 195 &m->peer.peers->peer[0].name, gr1->protocol); |
101 | 196 |
102 ngx_mysql_close(m, NGX_ERROR); | 197 ngx_mysql_close(m, NGX_ERROR); |
103 return; | 198 return; |
104 } | 199 } |
105 | 200 |
106 len = ngx_strlen(&p[5]); | 201 gr2 = (ngx_mysql_greeting2_pkt_t *) |
107 t = p + 5 + len + 1; | 202 (gr1->version + ngx_strlen(gr1->version) + 1); |
108 | 203 |
109 capacity = ngx_m16toh((&t[4 + 9])); | 204 capacity = ngx_m16toh(gr2->capacity); |
110 | 205 |
111 ngx_log_debug8(NGX_LOG_DEBUG_MYSQL, rev->log, 0, | 206 ngx_log_debug8(NGX_LOG_DEBUG_MYSQL, rev->log, 0, |
112 "mysql version: %ud, \"%s\", thread: %ud, salt: \"%s\", ", | 207 "mysql version: %ud, \"%s\", thread: %ud, salt: \"%s\", " |
113 "capacity: %Xd, charset: %ud, status: %ud, salt rest \"%s\"", | 208 "capacity: %Xd, charset: %ud, status: %ud, salt rest \"%s\"", |
114 p[4], &p[5], ngx_m32toh(t), &t[4], | 209 gr1->protocol, gr1->version, ngx_m32toh(gr2->thread), |
115 capacity, t[4 + 9 + 2], | 210 gr2->salt1, capacity, gr2->charset, |
116 ngx_m16toh((&t[4 + 9 + 2 + 1])), | 211 ngx_m16toh(gr2->status), &gr2->salt2); |
117 t[4 + 9 + 2 + 1 + 2 + 13]); | 212 |
118 | 213 capacity = NGX_MYSQL_LONG_PASSWORD |
119 capacity &= NGX_MYSQL_LONG_PASSWORD | 214 | NGX_MYSQL_CONNECT_WITH_DB |
120 | NGX_MYSQL_CONNECT_WITH_DB | 215 | NGX_MYSQL_PROTOCOL_41 |
121 | NGX_MYSQL_PROTOCOL_41; | 216 | NGX_MYSQL_SECURE_CONNECTION; |
122 | 217 |
218 len = 4 + 4 + 4 + 1 + 23 + m->login->len + 1 + 1 + m->database->len + 1; | |
219 | |
220 if (m->passwd->len) { | |
221 len += 20; | |
222 } | |
223 | |
224 auth = ngx_palloc(m->pool, len); | |
225 if (auth == NULL) { | |
226 ngx_mysql_close(m, NGX_ERROR); | |
227 return; | |
228 } | |
229 | |
230 ngx_htom24(auth->pktlen, len - 4); | |
231 auth->pktn = (u_char) (gr1->pktn + 1); | |
232 | |
233 ngx_htom32(auth->capacity, capacity); | |
234 ngx_htom32(auth->max_packet, 0x01000000); /* max packet size 2^24 */ | |
235 ngx_memzero(auth->zero, 24); | |
236 auth->charset = gr2->charset; | |
237 | |
238 p = ngx_copy(auth->login, m->login->data, m->login->len); | |
239 *p++ = '\0'; | |
240 | |
241 if (m->passwd->len) { | |
242 | |
243 *p++ = (u_char) 20; | |
244 | |
245 SHA1_Init(&sha); | |
246 SHA1_Update(&sha, m->passwd->data, m->passwd->len); | |
247 SHA1_Final(hash1, &sha); | |
248 | |
249 SHA1_Init(&sha); | |
250 SHA1_Update(&sha, hash1, 20); | |
251 SHA1_Final(hash2, &sha); | |
252 | |
253 SHA1_Init(&sha); | |
254 SHA1_Update(&sha, gr2->salt1, 8); | |
255 SHA1_Update(&sha, gr2->salt2, 12); | |
256 SHA1_Update(&sha, hash2, 20); | |
257 SHA1_Final(hash2, &sha); | |
258 | |
259 for (i = 0; i < 20; i++) { | |
260 *p++ = (u_char) (hash1[i] ^ hash2[i]); | |
261 } | |
262 | |
263 } else { | |
264 *p++ = '\0'; | |
265 } | |
266 | |
267 p = ngx_copy(p, m->database->data, m->database->len); | |
268 *p = '\0'; | |
269 | |
270 | |
271 n = ngx_send(m->peer.connection, (void *) auth, len); | |
272 | |
273 if (n < (ssize_t) len) { | |
274 ngx_log_error(NGX_LOG_ERR, rev->log, 0, | |
275 "the incomplete packet was sent to mysql server %V", | |
276 &m->peer.peers->peer[0].name); | |
277 | |
278 ngx_mysql_close(m, NGX_ERROR); | |
279 return; | |
280 } | |
281 | |
282 m->peer.connection->read->handler = ngx_mysql_read_auth_result; | |
283 | |
284 ngx_add_timer(m->peer.connection->read, /* STUB */ 5000); | |
285 } | |
286 | |
287 | |
288 static void | |
289 ngx_mysql_empty_handler(ngx_event_t *wev) | |
290 { | |
291 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, wev->log, 0, "mysql empty handler"); | |
292 | |
293 return; | |
294 } | |
295 | |
296 | |
297 static void | |
298 ngx_mysql_read_auth_result(ngx_event_t *rev) | |
299 { | |
300 ssize_t n, len; | |
301 ngx_str_t msg; | |
302 ngx_mysql_t *m; | |
303 ngx_connection_t *c; | |
304 ngx_mysql_error_pkt_t *epkt; | |
305 ngx_mysql_response_pkt_t *pkt; | |
306 | |
307 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0, "mysql read auth"); | |
308 | |
309 c = rev->data; | |
310 m = c->data; | |
311 | |
312 m->peer.log->action = "reading mysql auth result"; | |
313 | |
314 n = ngx_recv(m->peer.connection, m->buf->pos, /* STUB */ 1024); | |
315 | |
316 if (n == NGX_AGAIN) { | |
317 return; | |
318 } | |
319 | |
320 if (n < 5) { | |
321 ngx_mysql_close(m, NGX_ERROR); | |
322 return; | |
323 } | |
324 | |
325 pkt = (ngx_mysql_response_pkt_t *) m->buf->pos; | |
326 | |
327 len = ngx_m24toh(pkt->pktlen); | |
328 | |
329 if (len > n - 4) { | |
330 ngx_log_error(NGX_LOG_ERR, rev->log, 0, | |
331 "mysql server %V sent incomplete response packet", | |
332 &m->peer.peers->peer[0].name); | |
333 | |
334 ngx_mysql_close(m, NGX_ERROR); | |
335 return; | |
336 } | |
337 | |
338 if (pkt->fields == 0) { | |
339 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0, "mysql auth OK"); | |
340 | |
341 m->state = NGX_OK; | |
342 m->pktn = 0; | |
343 | |
344 m->handler(m); | |
345 | |
346 return; | |
347 } | |
348 | |
349 epkt = (ngx_mysql_error_pkt_t *) pkt; | |
350 | |
351 msg.len = (u_char *) epkt + 4 + len - epkt->message; | |
352 msg.data = epkt->message; | |
353 | |
354 ngx_log_error(NGX_LOG_ERR, rev->log, 0, | |
355 "mysql server %V sent error (%ud): \"%V\"", | |
356 &m->peer.peers->peer[0].name, ngx_m16toh(epkt->code), &msg); | |
357 | |
358 ngx_mysql_close(m, NGX_ERROR); | |
359 } | |
360 | |
361 | |
362 ngx_int_t | |
363 ngx_mysql_query(ngx_mysql_t *m) | |
364 { | |
365 ssize_t n; | |
366 ngx_mysql_command_pkt_t *pkt; | |
367 | |
368 pkt = (ngx_mysql_command_pkt_t *) m->query.data; | |
369 | |
370 ngx_htom24(pkt->pktlen, m->query.len - 4); | |
371 pkt->pktn = (u_char) m->pktn++; | |
372 pkt->command = NGX_MYSQL_CMD_QUERY; | |
373 | |
374 n = ngx_send(m->peer.connection, m->query.data, m->query.len); | |
375 | |
376 if (n < (ssize_t) m->query.len) { | |
377 ngx_log_error(NGX_LOG_ERR, m->peer.log, 0, | |
378 "the incomplete packet was sent to mysql server %V", | |
379 &m->peer.peers->peer[0].name); | |
380 | |
381 ngx_mysql_close(m, NGX_ERROR); | |
382 return NGX_OK; | |
383 } | |
384 | |
385 m->peer.connection->read->handler = ngx_mysql_read_query_result; | |
386 | |
387 ngx_add_timer(m->peer.connection->read, /* STUB */ 5000); | |
388 | |
389 /* STUB handle event */ | |
390 | |
391 return NGX_OK; | |
392 } | |
393 | |
394 | |
395 static void | |
396 ngx_mysql_read_query_result(ngx_event_t *rev) | |
397 { | |
398 ssize_t n, len; | |
399 ngx_str_t msg; | |
400 ngx_mysql_t *m; | |
401 ngx_connection_t *c; | |
402 ngx_mysql_error_pkt_t *epkt; | |
403 ngx_mysql_response_pkt_t *pkt; | |
404 | |
405 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0, "mysql read query result"); | |
406 | |
407 c = rev->data; | |
408 m = c->data; | |
409 | |
410 m->peer.log->action = "reading mysql read query result"; | |
411 | |
412 n = ngx_recv(m->peer.connection, m->buf->pos, /* STUB */ 1024); | |
413 | |
414 if (n == NGX_AGAIN) { | |
415 return; | |
416 } | |
417 | |
418 if (n < 5) { | |
419 ngx_mysql_close(m, NGX_ERROR); | |
420 return; | |
421 } | |
422 | |
423 pkt = (ngx_mysql_response_pkt_t *) m->buf->pos; | |
424 | |
425 len = ngx_m24toh(pkt->pktlen); | |
426 | |
427 if (len > n - 4) { | |
428 ngx_log_error(NGX_LOG_ERR, rev->log, 0, | |
429 "mysql server %V sent incomplete response packet", | |
430 &m->peer.peers->peer[0].name); | |
431 | |
432 ngx_mysql_close(m, NGX_ERROR); | |
433 return; | |
434 } | |
435 | |
436 if (pkt->fields != 0xff) { | |
437 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0, "mysql query OK"); | |
438 | |
439 m->state = NGX_OK; | |
440 m->pktn = pkt->pktn; | |
441 | |
442 m->handler(m); | |
443 | |
444 return; | |
445 } | |
446 | |
447 epkt = (ngx_mysql_error_pkt_t *) pkt; | |
448 | |
449 msg.len = (u_char *) epkt + 4 + len - epkt->message; | |
450 msg.data = epkt->message; | |
451 | |
452 ngx_log_error(NGX_LOG_ERR, rev->log, 0, | |
453 "mysql server %V sent error (%ud): \"%V\"", | |
454 &m->peer.peers->peer[0].name, ngx_m16toh(epkt->code), &msg); | |
455 | |
456 ngx_mysql_close(m, NGX_ERROR); | |
123 } | 457 } |
124 | 458 |
125 | 459 |
126 static void | 460 static void |
127 ngx_mysql_close(ngx_mysql_t *m, ngx_int_t rc) | 461 ngx_mysql_close(ngx_mysql_t *m, ngx_int_t rc) |