@ -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_empt y_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 ) ;
}