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)