Mercurial > hg > nginx-quic
annotate src/mysql/ngx_mysql.c @ 4513:2dfa4d76e4c5 stable-1.0
Merge of r4491, r4492:
*) Renamed constants and fixed off-by-one error in "msie_padding on"
handling.
*) Added support for the 307 Temporary Redirect.
author | Maxim Dounin <mdounin@mdounin.ru> |
---|---|
date | Mon, 05 Mar 2012 13:03:39 +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 } |