1 /*
2 +----------------------------------------------------------------------+
3 | PHP Version 7 |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2006-2018 The PHP Group |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
15 | Authors: Andrey Hristov <andrey@php.net> |
16 | Ulf Wendel <uw@php.net> |
17 | Georg Richter <georg@php.net> |
18 +----------------------------------------------------------------------+
19 */
20 #include "php.h"
21 #include "mysqlnd.h"
22 #include "mysqlnd_wireprotocol.h"
23 #include "mysqlnd_priv.h"
24 #include "mysqlnd_debug.h"
25
26 /* {{{ mysqlnd_local_infile_init */
27 static
mysqlnd_local_infile_init(void ** ptr,const char * const filename)28 int mysqlnd_local_infile_init(void ** ptr, const char * const filename)
29 {
30 MYSQLND_INFILE_INFO *info;
31 php_stream_context *context = NULL;
32
33 DBG_ENTER("mysqlnd_local_infile_init");
34
35 info = ((MYSQLND_INFILE_INFO *)mnd_ecalloc(1, sizeof(MYSQLND_INFILE_INFO)));
36 if (!info) {
37 DBG_RETURN(1);
38 }
39
40 *ptr = info;
41
42 /* check open_basedir */
43 if (PG(open_basedir)) {
44 if (php_check_open_basedir_ex(filename, 0) == -1) {
45 strcpy(info->error_msg, "open_basedir restriction in effect. Unable to open file");
46 info->error_no = CR_UNKNOWN_ERROR;
47 DBG_RETURN(1);
48 }
49 }
50
51 info->filename = filename;
52 info->fd = php_stream_open_wrapper_ex((char *)filename, "r", 0, NULL, context);
53
54 if (info->fd == NULL) {
55 snprintf((char *)info->error_msg, sizeof(info->error_msg), "Can't find file '%-.64s'.", filename);
56 info->error_no = MYSQLND_EE_FILENOTFOUND;
57 DBG_RETURN(1);
58 }
59
60 DBG_RETURN(0);
61 }
62 /* }}} */
63
64
65 /* {{{ mysqlnd_local_infile_read */
66 static
mysqlnd_local_infile_read(void * ptr,zend_uchar * buf,unsigned int buf_len)67 int mysqlnd_local_infile_read(void * ptr, zend_uchar * buf, unsigned int buf_len)
68 {
69 MYSQLND_INFILE_INFO *info = (MYSQLND_INFILE_INFO *)ptr;
70 int count;
71
72 DBG_ENTER("mysqlnd_local_infile_read");
73
74 count = (int) php_stream_read(info->fd, (char *) buf, buf_len);
75
76 if (count < 0) {
77 strcpy(info->error_msg, "Error reading file");
78 info->error_no = CR_UNKNOWN_ERROR;
79 }
80
81 DBG_RETURN(count);
82 }
83 /* }}} */
84
85
86 /* {{{ mysqlnd_local_infile_error */
87 static
mysqlnd_local_infile_error(void * ptr,char * error_buf,unsigned int error_buf_len)88 int mysqlnd_local_infile_error(void * ptr, char *error_buf, unsigned int error_buf_len)
89 {
90 MYSQLND_INFILE_INFO *info = (MYSQLND_INFILE_INFO *)ptr;
91
92 DBG_ENTER("mysqlnd_local_infile_error");
93
94 if (info) {
95 strlcpy(error_buf, info->error_msg, error_buf_len);
96 DBG_INF_FMT("have info, %d", info->error_no);
97 DBG_RETURN(info->error_no);
98 }
99
100 strlcpy(error_buf, "Unknown error", error_buf_len);
101 DBG_INF_FMT("no info, %d", CR_UNKNOWN_ERROR);
102 DBG_RETURN(CR_UNKNOWN_ERROR);
103 }
104 /* }}} */
105
106
107 /* {{{ mysqlnd_local_infile_end */
108 static
mysqlnd_local_infile_end(void * ptr)109 void mysqlnd_local_infile_end(void * ptr)
110 {
111 MYSQLND_INFILE_INFO *info = (MYSQLND_INFILE_INFO *)ptr;
112
113 if (info) {
114 /* php_stream_close segfaults on NULL */
115 if (info->fd) {
116 php_stream_close(info->fd);
117 info->fd = NULL;
118 }
119 mnd_efree(info);
120 }
121 }
122 /* }}} */
123
124
125 /* {{{ mysqlnd_local_infile_default */
126 void
mysqlnd_local_infile_default(MYSQLND_CONN_DATA * conn)127 mysqlnd_local_infile_default(MYSQLND_CONN_DATA * conn)
128 {
129 conn->infile.local_infile_init = mysqlnd_local_infile_init;
130 conn->infile.local_infile_read = mysqlnd_local_infile_read;
131 conn->infile.local_infile_error = mysqlnd_local_infile_error;
132 conn->infile.local_infile_end = mysqlnd_local_infile_end;
133 }
134 /* }}} */
135
136
137 static const char *lost_conn = "Lost connection to MySQL server during LOAD DATA of a local file";
138
139
140 /* {{{ mysqlnd_handle_local_infile */
141 enum_func_status
mysqlnd_handle_local_infile(MYSQLND_CONN_DATA * conn,const char * const filename,zend_bool * is_warning)142 mysqlnd_handle_local_infile(MYSQLND_CONN_DATA * conn, const char * const filename, zend_bool * is_warning)
143 {
144 zend_uchar *buf = NULL;
145 zend_uchar empty_packet[MYSQLND_HEADER_SIZE];
146 enum_func_status result = FAIL;
147 unsigned int buflen = 4096;
148 void *info = NULL;
149 int bufsize;
150 size_t ret;
151 MYSQLND_INFILE infile;
152 MYSQLND_PFC * net = conn->protocol_frame_codec;
153 MYSQLND_VIO * vio = conn->vio;
154
155 DBG_ENTER("mysqlnd_handle_local_infile");
156
157 if (!(conn->options->flags & CLIENT_LOCAL_FILES)) {
158 php_error_docref(NULL, E_WARNING, "LOAD DATA LOCAL INFILE forbidden");
159 /* write empty packet to server */
160 ret = net->data->m.send(net, vio, empty_packet, 0, conn->stats, conn->error_info);
161 *is_warning = TRUE;
162 goto infile_error;
163 }
164
165 infile = conn->infile;
166 /* allocate buffer for reading data */
167 buf = (zend_uchar *) mnd_ecalloc(1, buflen);
168
169 *is_warning = FALSE;
170
171 /* init handler: allocate read buffer and open file */
172 if (infile.local_infile_init(&info, (char *)filename)) {
173 char tmp_buf[sizeof(conn->error_info->error)];
174 int tmp_error_no;
175 *is_warning = TRUE;
176 /* error occurred */
177 tmp_error_no = infile.local_infile_error(info, tmp_buf, sizeof(tmp_buf));
178 SET_CLIENT_ERROR(conn->error_info, tmp_error_no, UNKNOWN_SQLSTATE, tmp_buf);
179 /* write empty packet to server */
180 ret = net->data->m.send(net, vio, empty_packet, 0, conn->stats, conn->error_info);
181 goto infile_error;
182 }
183
184 /* read data */
185 while ((bufsize = infile.local_infile_read (info, buf + MYSQLND_HEADER_SIZE, buflen - MYSQLND_HEADER_SIZE)) > 0) {
186 if ((ret = net->data->m.send(net, vio, buf, bufsize, conn->stats, conn->error_info)) == 0) {
187 DBG_ERR_FMT("Error during read : %d %s %s", CR_SERVER_LOST, UNKNOWN_SQLSTATE, lost_conn);
188 SET_CLIENT_ERROR(conn->error_info, CR_SERVER_LOST, UNKNOWN_SQLSTATE, lost_conn);
189 goto infile_error;
190 }
191 }
192
193 /* send empty packet for eof */
194 if ((ret = net->data->m.send(net, vio, empty_packet, 0, conn->stats, conn->error_info)) == 0) {
195 SET_CLIENT_ERROR(conn->error_info, CR_SERVER_LOST, UNKNOWN_SQLSTATE, lost_conn);
196 goto infile_error;
197 }
198
199 /* error during read occurred */
200 if (bufsize < 0) {
201 char tmp_buf[sizeof(conn->error_info->error)];
202 int tmp_error_no;
203 *is_warning = TRUE;
204 DBG_ERR_FMT("Bufsize < 0, warning, %d %s %s", CR_SERVER_LOST, UNKNOWN_SQLSTATE, lost_conn);
205 tmp_error_no = infile.local_infile_error(info, tmp_buf, sizeof(tmp_buf));
206 SET_CLIENT_ERROR(conn->error_info, tmp_error_no, UNKNOWN_SQLSTATE, tmp_buf);
207 goto infile_error;
208 }
209
210 result = PASS;
211
212 infile_error:
213 /* get response from server and update upsert values */
214 if (FAIL == conn->payload_decoder_factory->m.send_command_handle_response(
215 conn->payload_decoder_factory,
216 PROT_OK_PACKET, FALSE, COM_QUERY, FALSE,
217 conn->error_info,
218 conn->upsert_status,
219 &conn->last_message,
220 conn->persistent)) {
221 result = FAIL;
222 }
223
224 (*conn->infile.local_infile_end)(info);
225 if (buf) {
226 mnd_efree(buf);
227 }
228 DBG_INF_FMT("%s", result == PASS? "PASS":"FAIL");
229 DBG_RETURN(result);
230 }
231 /* }}} */
232
233 /*
234 * Local variables:
235 * tab-width: 4
236 * c-basic-offset: 4
237 * End:
238 * vim600: noet sw=4 ts=4 fdm=marker
239 * vim<600: noet sw=4 ts=4
240 */
241