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