1 | /************************************************************************************ |
2 | Copyright (C) 2015,2016 MariaDB Corporation AB, |
3 | |
4 | This library is free software; you can redistribute it and/or |
5 | modify it under the terms of the GNU Library General Public |
6 | License as published by the Free Software Foundation; either |
7 | version 2 of the License, or (at your option) any later version. |
8 | |
9 | This library is distributed in the hope that it will be useful, |
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
12 | Library General Public License for more details. |
13 | |
14 | You should have received a copy of the GNU Library General Public |
15 | License along with this library; if not see <http://www.gnu.org/licenses> |
16 | or write to the Free Software Foundation, Inc., |
17 | 51 Franklin St., Fifth Floor, Boston, MA 02110, USA |
18 | *************************************************************************************/ |
19 | |
20 | /* |
21 | MariaDB virtual IO plugin for socket communication: |
22 | |
23 | The plugin handles connections via unix and network sockets. it is enabled by |
24 | default and compiled into Connector/C. |
25 | */ |
26 | |
27 | #include <ma_global.h> |
28 | #include <ma_sys.h> |
29 | #include <errmsg.h> |
30 | #include <mysql.h> |
31 | #include <mysql/client_plugin.h> |
32 | #include <ma_context.h> |
33 | #include <mariadb_async.h> |
34 | #include <ma_common.h> |
35 | #include <string.h> |
36 | #include <time.h> |
37 | #ifndef _WIN32 |
38 | #ifdef HAVE_SYS_UN_H |
39 | #include <sys/un.h> |
40 | #endif |
41 | #ifdef HAVE_POLL |
42 | #include <sys/poll.h> |
43 | #endif |
44 | #ifdef HAVE_SYS_IOCTL_H |
45 | #include <sys/ioctl.h> |
46 | #endif |
47 | #ifdef HAVE_FCNTL_H |
48 | #include <fcntl.h> |
49 | #endif |
50 | #include <netinet/in_systm.h> |
51 | #include <netinet/in.h> |
52 | #include <netinet/ip.h> |
53 | #include <netdb.h> |
54 | #include <netinet/tcp.h> |
55 | #define IS_SOCKET_EINTR(err) (err == SOCKET_EINTR) |
56 | #else |
57 | #include <ws2tcpip.h> |
58 | #define O_NONBLOCK 1 |
59 | #define MSG_DONTWAIT 0 |
60 | #define IS_SOCKET_EINTR(err) 0 |
61 | #endif |
62 | |
63 | #ifndef SOCKET_ERROR |
64 | #define SOCKET_ERROR -1 |
65 | #endif |
66 | |
67 | #ifndef INVALID_SOCKET |
68 | #define INVALID_SOCKET -1 |
69 | #endif |
70 | |
71 | #define DNS_TIMEOUT 30 |
72 | |
73 | #ifndef O_NONBLOCK |
74 | #if defined(O_NDELAY) |
75 | #define O_NONBLOCK O_NODELAY |
76 | #elif defined (O_FNDELAY) |
77 | #define O_NONBLOCK O_FNDELAY |
78 | #else |
79 | #error socket blocking is not supported on this platform |
80 | #endif |
81 | #endif |
82 | |
83 | #if SOCKET_EAGAIN != SOCKET_EWOULDBLOCK |
84 | #define HAVE_SOCKET_EWOULDBLOCK 1 |
85 | #endif |
86 | |
87 | /* Function prototypes */ |
88 | my_bool pvio_socket_set_timeout(MARIADB_PVIO *pvio, enum enum_pvio_timeout type, int timeout); |
89 | int pvio_socket_get_timeout(MARIADB_PVIO *pvio, enum enum_pvio_timeout type); |
90 | ssize_t pvio_socket_read(MARIADB_PVIO *pvio, uchar *buffer, size_t length); |
91 | ssize_t pvio_socket_async_read(MARIADB_PVIO *pvio, uchar *buffer, size_t length); |
92 | ssize_t pvio_socket_async_write(MARIADB_PVIO *pvio, const uchar *buffer, size_t length); |
93 | ssize_t pvio_socket_write(MARIADB_PVIO *pvio, const uchar *buffer, size_t length); |
94 | int pvio_socket_wait_io_or_timeout(MARIADB_PVIO *pvio, my_bool is_read, int timeout); |
95 | int pvio_socket_blocking(MARIADB_PVIO *pvio, my_bool value, my_bool *old_value); |
96 | my_bool pvio_socket_connect(MARIADB_PVIO *pvio, MA_PVIO_CINFO *cinfo); |
97 | my_bool pvio_socket_close(MARIADB_PVIO *pvio); |
98 | int pvio_socket_fast_send(MARIADB_PVIO *pvio); |
99 | int pvio_socket_keepalive(MARIADB_PVIO *pvio); |
100 | my_bool pvio_socket_get_handle(MARIADB_PVIO *pvio, void *handle); |
101 | my_bool pvio_socket_is_blocking(MARIADB_PVIO *pvio); |
102 | my_bool pvio_socket_is_alive(MARIADB_PVIO *pvio); |
103 | my_bool pvio_socket_has_data(MARIADB_PVIO *pvio, ssize_t *data_len); |
104 | int pvio_socket_shutdown(MARIADB_PVIO *pvio); |
105 | |
106 | static int pvio_socket_init(char *unused1, |
107 | size_t unused2, |
108 | int unused3, |
109 | va_list); |
110 | static int pvio_socket_end(void); |
111 | static ssize_t ma_send(my_socket socket, const uchar *buffer, size_t length, int flags); |
112 | static ssize_t ma_recv(my_socket socket, uchar *buffer, size_t length, int flags); |
113 | |
114 | struct st_ma_pvio_methods pvio_socket_methods= { |
115 | pvio_socket_set_timeout, |
116 | pvio_socket_get_timeout, |
117 | pvio_socket_read, |
118 | pvio_socket_async_read, |
119 | pvio_socket_write, |
120 | pvio_socket_async_write, |
121 | pvio_socket_wait_io_or_timeout, |
122 | pvio_socket_blocking, |
123 | pvio_socket_connect, |
124 | pvio_socket_close, |
125 | pvio_socket_fast_send, |
126 | pvio_socket_keepalive, |
127 | pvio_socket_get_handle, |
128 | pvio_socket_is_blocking, |
129 | pvio_socket_is_alive, |
130 | pvio_socket_has_data, |
131 | pvio_socket_shutdown |
132 | }; |
133 | |
134 | #ifndef PLUGIN_DYNAMIC |
135 | MARIADB_PVIO_PLUGIN pvio_socket_client_plugin= |
136 | #else |
137 | MARIADB_PVIO_PLUGIN _mysql_client_plugin_declaration_ |
138 | #endif |
139 | { |
140 | MARIADB_CLIENT_PVIO_PLUGIN, |
141 | MARIADB_CLIENT_PVIO_PLUGIN_INTERFACE_VERSION, |
142 | "pvio_socket" , |
143 | "Georg Richter" , |
144 | "MariaDB virtual IO plugin for socket communication" , |
145 | {1, 0, 0}, |
146 | "LGPL" , |
147 | NULL, |
148 | &pvio_socket_init, |
149 | &pvio_socket_end, |
150 | NULL, |
151 | &pvio_socket_methods |
152 | }; |
153 | |
154 | struct st_pvio_socket { |
155 | my_socket socket; |
156 | int fcntl_mode; |
157 | MYSQL *mysql; |
158 | }; |
159 | |
160 | static my_bool pvio_socket_initialized= FALSE; |
161 | |
162 | static int pvio_socket_init(char *errmsg __attribute__((unused)), |
163 | size_t errmsg_length __attribute__((unused)), |
164 | int unused __attribute__((unused)), |
165 | va_list va __attribute__((unused))) |
166 | { |
167 | pvio_socket_initialized= TRUE; |
168 | return 0; |
169 | } |
170 | |
171 | static int pvio_socket_end(void) |
172 | { |
173 | if (!pvio_socket_initialized) |
174 | return 1; |
175 | return 0; |
176 | } |
177 | |
178 | my_bool pvio_socket_change_timeout(MARIADB_PVIO *pvio, enum enum_pvio_timeout type, int timeout) |
179 | { |
180 | struct timeval tm; |
181 | int rc= 0; |
182 | struct st_pvio_socket *csock= NULL; |
183 | if (!pvio) |
184 | return 1; |
185 | if (!(csock= (struct st_pvio_socket *)pvio->data)) |
186 | return 1; |
187 | tm.tv_sec= timeout / 1000; |
188 | tm.tv_usec= (timeout % 1000) * 1000; |
189 | switch(type) |
190 | { |
191 | case PVIO_WRITE_TIMEOUT: |
192 | #ifndef _WIN32 |
193 | rc= setsockopt(csock->socket, SOL_SOCKET, SO_SNDTIMEO, (const char *)&tm, sizeof(tm)); |
194 | #else |
195 | rc= setsockopt(csock->socket, SOL_SOCKET, SO_SNDTIMEO, (const char *)&timeout, sizeof(int)); |
196 | #endif |
197 | break; |
198 | case PVIO_READ_TIMEOUT: |
199 | #ifndef _WIN32 |
200 | rc= setsockopt(csock->socket, SOL_SOCKET, SO_RCVTIMEO, (const char *)&tm, sizeof(tm)); |
201 | #else |
202 | rc= setsockopt(csock->socket, SOL_SOCKET, SO_RCVTIMEO, (const char *)&timeout, sizeof(int)); |
203 | #endif |
204 | break; |
205 | default: |
206 | break; |
207 | } |
208 | return rc; |
209 | } |
210 | |
211 | /* {{{ pvio_socket_set_timeout */ |
212 | /* |
213 | set timeout value |
214 | |
215 | SYNOPSIS |
216 | pvio_socket_set_timeout |
217 | pvio PVIO |
218 | type timeout type (connect, read, write) |
219 | timeout timeout in seconds |
220 | |
221 | DESCRIPTION |
222 | Sets timeout values for connection-, read or write time out. |
223 | PVIO internally stores all timeout values in milliseconds, but |
224 | accepts and returns all time values in seconds (like api does). |
225 | |
226 | RETURNS |
227 | 0 Success |
228 | 1 Error |
229 | */ |
230 | my_bool pvio_socket_set_timeout(MARIADB_PVIO *pvio, enum enum_pvio_timeout type, int timeout) |
231 | { |
232 | struct st_pvio_socket *csock= NULL; |
233 | if (!pvio) |
234 | return 1; |
235 | csock= (struct st_pvio_socket *)pvio->data; |
236 | pvio->timeout[type]= (timeout > 0) ? timeout * 1000 : -1; |
237 | if (csock) |
238 | return pvio_socket_change_timeout(pvio, type, timeout * 1000); |
239 | return 0; |
240 | } |
241 | /* }}} */ |
242 | |
243 | /* {{{ pvio_socket_get_timeout */ |
244 | /* |
245 | get timeout value |
246 | |
247 | SYNOPSIS |
248 | pvio_socket_get_timeout |
249 | pvio PVIO |
250 | type timeout type (connect, read, write) |
251 | |
252 | DESCRIPTION |
253 | Returns timeout values for connection-, read or write time out. |
254 | PVIO internally stores all timeout values in milliseconds, but |
255 | accepts and returns all time values in seconds (like api does). |
256 | |
257 | RETURNS |
258 | 0...n time out value |
259 | -1 error |
260 | */ |
261 | int pvio_socket_get_timeout(MARIADB_PVIO *pvio, enum enum_pvio_timeout type) |
262 | { |
263 | if (!pvio) |
264 | return -1; |
265 | return pvio->timeout[type] / 1000; |
266 | } |
267 | /* }}} */ |
268 | |
269 | /* {{{ pvio_socket_read */ |
270 | /* |
271 | read from socket |
272 | |
273 | SYNOPSIS |
274 | pvio_socket_read() |
275 | pvio PVIO |
276 | buffer read buffer |
277 | length buffer length |
278 | |
279 | DESCRIPTION |
280 | reads up to length bytes into specified buffer. In the event of an |
281 | error erno is set to indicate it. |
282 | |
283 | RETURNS |
284 | 1..n number of bytes read |
285 | 0 peer has performed shutdown |
286 | -1 on error |
287 | |
288 | */ |
289 | ssize_t pvio_socket_read(MARIADB_PVIO *pvio, uchar *buffer, size_t length) |
290 | { |
291 | ssize_t r; |
292 | int read_flags= MSG_DONTWAIT; |
293 | struct st_pvio_socket *csock; |
294 | int timeout; |
295 | |
296 | if (!pvio || !pvio->data) |
297 | return -1; |
298 | |
299 | csock= (struct st_pvio_socket *)pvio->data; |
300 | timeout = pvio->timeout[PVIO_READ_TIMEOUT]; |
301 | |
302 | while ((r = ma_recv(csock->socket, (void *)buffer, length, read_flags)) == -1) |
303 | { |
304 | int err = socket_errno; |
305 | if ((err != SOCKET_EAGAIN |
306 | #ifdef HAVE_SOCKET_EWOULDBLOCK |
307 | && err != SOCKET_EWOULDBLOCK |
308 | #endif |
309 | ) || timeout == 0) |
310 | return r; |
311 | |
312 | if (pvio_socket_wait_io_or_timeout(pvio, TRUE, timeout) < 1) |
313 | return -1; |
314 | } |
315 | return r; |
316 | } |
317 | /* }}} */ |
318 | |
319 | /* {{{ pvio_socket_async_read */ |
320 | /* |
321 | read from socket |
322 | |
323 | SYNOPSIS |
324 | pvio_socket_async_read() |
325 | pvio PVIO |
326 | buffer read buffer |
327 | length buffer length |
328 | |
329 | DESCRIPTION |
330 | reads up to length bytes into specified buffer. In the event of an |
331 | error erno is set to indicate it. |
332 | |
333 | RETURNS |
334 | 1..n number of bytes read |
335 | 0 peer has performed shutdown |
336 | -1 on error |
337 | |
338 | */ |
339 | ssize_t pvio_socket_async_read(MARIADB_PVIO *pvio, uchar *buffer, size_t length) |
340 | { |
341 | ssize_t r= -1; |
342 | #ifndef _WIN32 |
343 | int read_flags= MSG_DONTWAIT; |
344 | #endif |
345 | struct st_pvio_socket *csock= NULL; |
346 | |
347 | if (!pvio || !pvio->data) |
348 | return -1; |
349 | |
350 | csock= (struct st_pvio_socket *)pvio->data; |
351 | |
352 | #ifndef _WIN32 |
353 | r= recv(csock->socket,(void *)buffer, length, read_flags); |
354 | #else |
355 | /* Windows doesn't support MSG_DONTWAIT, so we need to set |
356 | socket to non blocking */ |
357 | pvio_socket_blocking(pvio, 0, 0); |
358 | r= recv(csock->socket, (char *)buffer, (int)length, 0); |
359 | #endif |
360 | return r; |
361 | } |
362 | /* }}} */ |
363 | |
364 | static ssize_t ma_send(my_socket socket, const uchar *buffer, size_t length, int flags) |
365 | { |
366 | ssize_t r; |
367 | #if !defined(MSG_NOSIGNAL) && !defined(SO_NOSIGPIPE) && !defined(_WIN32) |
368 | struct sigaction act, oldact; |
369 | act.sa_handler= SIG_IGN; |
370 | sigaction(SIGPIPE, &act, &oldact); |
371 | #endif |
372 | do { |
373 | r = send(socket, (const char *)buffer, IF_WIN((int)length,length), flags); |
374 | } |
375 | while (r == -1 && IS_SOCKET_EINTR(socket_errno)); |
376 | #if !defined(MSG_NOSIGNAL) && !defined(SO_NOSIGPIPE) && !defined(_WIN32) |
377 | sigaction(SIGPIPE, &oldact, NULL); |
378 | #endif |
379 | return r; |
380 | } |
381 | |
382 | static ssize_t ma_recv(my_socket socket, uchar *buffer, size_t length, int flags) |
383 | { |
384 | ssize_t r; |
385 | do { |
386 | r = recv(socket, (char*) buffer, IF_WIN((int)length, length), flags); |
387 | } |
388 | while (r == -1 && IS_SOCKET_EINTR(socket_errno)); |
389 | return r; |
390 | } |
391 | |
392 | /* {{{ pvio_socket_async_write */ |
393 | /* |
394 | write to socket |
395 | |
396 | SYNOPSIS |
397 | pvio_socket_async_write() |
398 | pvio PVIO |
399 | buffer read buffer |
400 | length buffer length |
401 | |
402 | DESCRIPTION |
403 | writes up to length bytes to socket. In the event of an |
404 | error erno is set to indicate it. |
405 | |
406 | RETURNS |
407 | 1..n number of bytes read |
408 | 0 peer has performed shutdown |
409 | -1 on error |
410 | |
411 | */ |
412 | ssize_t pvio_socket_async_write(MARIADB_PVIO *pvio, const uchar *buffer, size_t length) |
413 | { |
414 | ssize_t r= -1; |
415 | struct st_pvio_socket *csock= NULL; |
416 | #ifndef _WIN32 |
417 | int write_flags= MSG_DONTWAIT; |
418 | #ifdef MSG_NOSIGNAL |
419 | write_flags|= MSG_NOSIGNAL; |
420 | #endif |
421 | #endif |
422 | |
423 | if (!pvio || !pvio->data) |
424 | return -1; |
425 | |
426 | csock= (struct st_pvio_socket *)pvio->data; |
427 | |
428 | #ifndef WIN32 |
429 | r= ma_send(csock->socket, buffer, length, write_flags); |
430 | #else |
431 | /* Windows doesn't support MSG_DONTWAIT, so we need to set |
432 | socket to non blocking */ |
433 | pvio_socket_blocking(pvio, 0, 0); |
434 | r= send(csock->socket, (const char *)buffer, (int)length, 0); |
435 | #endif |
436 | |
437 | return r; |
438 | } |
439 | /* }}} */ |
440 | |
441 | /* {{{ pvio_socket_write */ |
442 | /* |
443 | write to socket |
444 | |
445 | SYNOPSIS |
446 | pvio_socket_write() |
447 | pvio PVIO |
448 | buffer read buffer |
449 | length buffer length |
450 | |
451 | DESCRIPTION |
452 | writes up to length bytes to socket. In the event of an |
453 | error erno is set to indicate it. |
454 | |
455 | RETURNS |
456 | 1..n number of bytes read |
457 | 0 peer has performed shutdown |
458 | -1 on error |
459 | |
460 | */ |
461 | ssize_t pvio_socket_write(MARIADB_PVIO *pvio, const uchar *buffer, size_t length) |
462 | { |
463 | ssize_t r; |
464 | struct st_pvio_socket *csock; |
465 | int timeout; |
466 | int send_flags= MSG_DONTWAIT; |
467 | #ifdef MSG_NOSIGNAL |
468 | send_flags|= MSG_NOSIGNAL; |
469 | #endif |
470 | if (!pvio || !pvio->data) |
471 | return -1; |
472 | |
473 | csock= (struct st_pvio_socket *)pvio->data; |
474 | timeout = pvio->timeout[PVIO_WRITE_TIMEOUT]; |
475 | |
476 | while ((r = ma_send(csock->socket, (void *)buffer, length,send_flags)) == -1) |
477 | { |
478 | int err = socket_errno; |
479 | if ((err != SOCKET_EAGAIN |
480 | #ifdef HAVE_SOCKET_EWOULDBLOCK |
481 | && err != SOCKET_EWOULDBLOCK |
482 | #endif |
483 | )|| timeout == 0) |
484 | return r; |
485 | if (pvio_socket_wait_io_or_timeout(pvio, FALSE, timeout) < 1) |
486 | return -1; |
487 | } |
488 | return r; |
489 | } |
490 | /* }}} */ |
491 | |
492 | int pvio_socket_wait_io_or_timeout(MARIADB_PVIO *pvio, my_bool is_read, int timeout) |
493 | { |
494 | int rc; |
495 | struct st_pvio_socket *csock= NULL; |
496 | |
497 | #ifndef _WIN32 |
498 | struct pollfd p_fd; |
499 | #else |
500 | struct timeval tv= {0,0}; |
501 | fd_set fds, exc_fds; |
502 | #endif |
503 | |
504 | if (!pvio || !pvio->data) |
505 | return 0; |
506 | |
507 | if (pvio->mysql->options.extension && |
508 | pvio->mysql->options.extension->io_wait != NULL) { |
509 | my_socket handle; |
510 | if (pvio_socket_get_handle(pvio, &handle)) |
511 | return 0; |
512 | return pvio->mysql->options.extension->io_wait(handle, is_read, timeout); |
513 | } |
514 | |
515 | csock= (struct st_pvio_socket *)pvio->data; |
516 | { |
517 | #ifndef _WIN32 |
518 | memset(&p_fd, 0, sizeof(p_fd)); |
519 | p_fd.fd= csock->socket; |
520 | p_fd.events= (is_read) ? POLLIN : POLLOUT; |
521 | |
522 | if (!timeout) |
523 | timeout= -1; |
524 | |
525 | do { |
526 | rc= poll(&p_fd, 1, timeout); |
527 | } while (rc == -1 && errno == EINTR); |
528 | |
529 | if (rc == 0) |
530 | errno= ETIMEDOUT; |
531 | #else |
532 | FD_ZERO(&fds); |
533 | FD_ZERO(&exc_fds); |
534 | |
535 | FD_SET(csock->socket, &fds); |
536 | FD_SET(csock->socket, &exc_fds); |
537 | |
538 | if (timeout >= 0) |
539 | { |
540 | tv.tv_sec= timeout / 1000; |
541 | tv.tv_usec= (timeout % 1000) * 1000; |
542 | } |
543 | |
544 | rc= select(0, (is_read) ? &fds : NULL, |
545 | (is_read) ? NULL : &fds, |
546 | &exc_fds, |
547 | (timeout >= 0) ? &tv : NULL); |
548 | |
549 | if (rc == SOCKET_ERROR) |
550 | { |
551 | errno= WSAGetLastError(); |
552 | } |
553 | else if (rc == 0) |
554 | { |
555 | rc= SOCKET_ERROR; |
556 | WSASetLastError(WSAETIMEDOUT); |
557 | errno= ETIMEDOUT; |
558 | } |
559 | else if (FD_ISSET(csock->socket, &exc_fds)) |
560 | { |
561 | int err; |
562 | int len = sizeof(int); |
563 | if (getsockopt(csock->socket, SOL_SOCKET, SO_ERROR, (char *)&err, &len) != SOCKET_ERROR) |
564 | { |
565 | WSASetLastError(err); |
566 | errno= err; |
567 | } |
568 | rc= SOCKET_ERROR; |
569 | } |
570 | |
571 | #endif |
572 | } |
573 | return rc; |
574 | } |
575 | |
576 | int pvio_socket_blocking(MARIADB_PVIO *pvio, my_bool block, my_bool *previous_mode) |
577 | { |
578 | my_bool is_blocking; |
579 | struct st_pvio_socket *csock; |
580 | int new_fcntl_mode; |
581 | |
582 | if (!pvio || !pvio->data) |
583 | return 1; |
584 | |
585 | csock = (struct st_pvio_socket *)pvio->data; |
586 | |
587 | is_blocking = !(csock->fcntl_mode & O_NONBLOCK); |
588 | if (previous_mode) |
589 | *previous_mode = is_blocking; |
590 | |
591 | if (is_blocking == block) |
592 | return 0; |
593 | |
594 | if (block) |
595 | new_fcntl_mode = csock->fcntl_mode & ~O_NONBLOCK; |
596 | else |
597 | new_fcntl_mode = csock->fcntl_mode | O_NONBLOCK; |
598 | |
599 | #ifdef _WIN32 |
600 | { |
601 | ulong arg = block ? 0 : 1; |
602 | if (ioctlsocket(csock->socket, FIONBIO, (void *)&arg)) |
603 | { |
604 | return(WSAGetLastError()); |
605 | } |
606 | } |
607 | #else |
608 | if (fcntl(csock->socket, F_SETFL, new_fcntl_mode) == -1) |
609 | { |
610 | return errno; |
611 | } |
612 | #endif |
613 | csock->fcntl_mode = new_fcntl_mode; |
614 | return 0; |
615 | } |
616 | |
617 | static int pvio_socket_internal_connect(MARIADB_PVIO *pvio, |
618 | const struct sockaddr *name, |
619 | size_t namelen) |
620 | { |
621 | int rc= 0; |
622 | struct st_pvio_socket *csock= NULL; |
623 | int timeout; |
624 | |
625 | if (!pvio || !pvio->data) |
626 | return 1; |
627 | |
628 | csock= (struct st_pvio_socket *)pvio->data; |
629 | timeout= pvio->timeout[PVIO_CONNECT_TIMEOUT]; |
630 | |
631 | /* set non blocking */ |
632 | pvio_socket_blocking(pvio, 0, 0); |
633 | |
634 | #ifndef _WIN32 |
635 | do { |
636 | rc= connect(csock->socket, (struct sockaddr*) name, (int)namelen); |
637 | } while (rc == -1 && (errno == EINTR || errno == EAGAIN)); |
638 | /* in case a timeout values was set we need to check error values |
639 | EINPROGRESS */ |
640 | if (timeout != 0 && rc == -1 && errno == EINPROGRESS) |
641 | { |
642 | rc= pvio_socket_wait_io_or_timeout(pvio, FALSE, timeout); |
643 | if (rc < 1) |
644 | return -1; |
645 | { |
646 | int error; |
647 | socklen_t error_len= sizeof(error); |
648 | if ((rc = getsockopt(csock->socket, SOL_SOCKET, SO_ERROR, |
649 | (char *)&error, &error_len)) < 0) |
650 | return errno; |
651 | else if (error) |
652 | return error; |
653 | } |
654 | } |
655 | #ifdef __APPLE__ |
656 | if (csock->socket) |
657 | { |
658 | int val= 1; |
659 | setsockopt(csock->socket, SOL_SOCKET, SO_NOSIGPIPE, (void *)&val, sizeof(int)); |
660 | } |
661 | #endif |
662 | #else |
663 | rc= connect(csock->socket, (struct sockaddr*) name, (int)namelen); |
664 | if (rc == SOCKET_ERROR) |
665 | { |
666 | if (WSAGetLastError() == WSAEWOULDBLOCK) |
667 | { |
668 | if (pvio_socket_wait_io_or_timeout(pvio, FALSE, timeout) < 0) |
669 | return -1; |
670 | rc= 0; |
671 | } |
672 | } |
673 | #endif |
674 | return rc; |
675 | } |
676 | |
677 | int pvio_socket_keepalive(MARIADB_PVIO *pvio) |
678 | { |
679 | int opt= 1; |
680 | struct st_pvio_socket *csock= NULL; |
681 | |
682 | if (!pvio || !pvio->data) |
683 | return 1; |
684 | |
685 | csock= (struct st_pvio_socket *)pvio->data; |
686 | |
687 | return setsockopt(csock->socket, SOL_SOCKET, SO_KEEPALIVE, |
688 | #ifndef _WIN32 |
689 | (const void *)&opt, sizeof(opt)); |
690 | #else |
691 | (char *)&opt, (int)sizeof(opt)); |
692 | #endif |
693 | } |
694 | |
695 | int pvio_socket_fast_send(MARIADB_PVIO *pvio) |
696 | { |
697 | int r= 0; |
698 | struct st_pvio_socket *csock= NULL; |
699 | |
700 | if (!pvio || !pvio->data) |
701 | return 1; |
702 | |
703 | csock= (struct st_pvio_socket *)pvio->data; |
704 | |
705 | /* Setting IP_TOS is not recommended on Windows. See |
706 | http://msdn.microsoft.com/en-us/library/windows/desktop/ms738586(v=vs.85).aspx |
707 | */ |
708 | #if !defined(_WIN32) && defined(IPTOS_THROUGHPUT) |
709 | { |
710 | int tos = IPTOS_THROUGHPUT; |
711 | r= setsockopt(csock->socket, IPPROTO_IP, IP_TOS, |
712 | (const void *)&tos, sizeof(tos)); |
713 | } |
714 | #endif /* !_WIN32 && IPTOS_THROUGHPUT */ |
715 | if (!r) |
716 | { |
717 | int opt = 1; |
718 | /* turn off nagle algorithm */ |
719 | r= setsockopt(csock->socket, IPPROTO_TCP, TCP_NODELAY, |
720 | #ifdef _WIN32 |
721 | (const char *)&opt, (int)sizeof(opt)); |
722 | #else |
723 | (const void *)&opt, sizeof(opt)); |
724 | #endif |
725 | } |
726 | return r; |
727 | } |
728 | |
729 | static int |
730 | pvio_socket_connect_sync_or_async(MARIADB_PVIO *pvio, |
731 | const struct sockaddr *name, uint namelen) |
732 | { |
733 | MYSQL *mysql= pvio->mysql; |
734 | if (mysql->options.extension && mysql->options.extension->async_context && |
735 | mysql->options.extension->async_context->active) |
736 | { |
737 | /* even if we are not connected yet, application needs to check socket |
738 | * via mysql_get_socket api call, so we need to assign pvio */ |
739 | mysql->options.extension->async_context->pvio= pvio; |
740 | pvio_socket_blocking(pvio, 0, 0); |
741 | return my_connect_async(pvio, name, namelen, pvio->timeout[PVIO_CONNECT_TIMEOUT]); |
742 | } |
743 | |
744 | return pvio_socket_internal_connect(pvio, name, namelen); |
745 | } |
746 | |
747 | my_bool pvio_socket_connect(MARIADB_PVIO *pvio, MA_PVIO_CINFO *cinfo) |
748 | { |
749 | struct st_pvio_socket *csock= NULL; |
750 | MYSQL *mysql; |
751 | |
752 | if (!pvio || !cinfo) |
753 | return 1; |
754 | |
755 | if (!(csock= (struct st_pvio_socket *)calloc(1, sizeof(struct st_pvio_socket)))) |
756 | { |
757 | PVIO_SET_ERROR(cinfo->mysql, CR_OUT_OF_MEMORY, unknown_sqlstate, 0, "" ); |
758 | return 1; |
759 | } |
760 | pvio->data= (void *)csock; |
761 | csock->socket= INVALID_SOCKET; |
762 | mysql= pvio->mysql= cinfo->mysql; |
763 | pvio->type= cinfo->type; |
764 | |
765 | if (cinfo->type == PVIO_TYPE_UNIXSOCKET) |
766 | { |
767 | #ifndef _WIN32 |
768 | #ifdef HAVE_SYS_UN_H |
769 | size_t port_length; |
770 | struct sockaddr_un UNIXaddr; |
771 | if ((csock->socket = socket(AF_UNIX,SOCK_STREAM,0)) == INVALID_SOCKET || |
772 | (port_length=strlen(cinfo->unix_socket)) >= (sizeof(UNIXaddr.sun_path))) |
773 | { |
774 | PVIO_SET_ERROR(cinfo->mysql, CR_SOCKET_CREATE_ERROR, unknown_sqlstate, 0, errno); |
775 | goto error; |
776 | } |
777 | memset((char*) &UNIXaddr, 0, sizeof(UNIXaddr)); |
778 | UNIXaddr.sun_family = AF_UNIX; |
779 | #if defined(__linux__) |
780 | /* Abstract socket */ |
781 | if (cinfo->unix_socket[0] == '@') |
782 | { |
783 | strncpy(UNIXaddr.sun_path + 1, cinfo->unix_socket + 1, 106); |
784 | port_length+= offsetof(struct sockaddr_un, sun_path); |
785 | } |
786 | else |
787 | #endif |
788 | { |
789 | strcpy(UNIXaddr.sun_path, cinfo->unix_socket); |
790 | port_length= sizeof(UNIXaddr); |
791 | } |
792 | if (pvio_socket_connect_sync_or_async(pvio, (struct sockaddr *) &UNIXaddr, port_length)) |
793 | { |
794 | PVIO_SET_ERROR(cinfo->mysql, CR_CONNECTION_ERROR, SQLSTATE_UNKNOWN, |
795 | ER(CR_CONNECTION_ERROR), cinfo->unix_socket, socket_errno); |
796 | goto error; |
797 | } |
798 | if (pvio_socket_blocking(pvio, 1, 0) == SOCKET_ERROR) |
799 | { |
800 | goto error; |
801 | } |
802 | #else |
803 | /* todo: error, not supported */ |
804 | #endif |
805 | #endif |
806 | } else if (cinfo->type == PVIO_TYPE_SOCKET) |
807 | { |
808 | struct addrinfo hints, *save_res= 0, *bind_res= 0, *res= 0, *bres= 0; |
809 | char server_port[NI_MAXSERV]; |
810 | int gai_rc; |
811 | int rc= 0; |
812 | time_t start_t= time(NULL); |
813 | #ifdef _WIN32 |
814 | DWORD wait_gai; |
815 | #else |
816 | unsigned int wait_gai; |
817 | #endif |
818 | |
819 | memset(&server_port, 0, NI_MAXSERV); |
820 | snprintf(server_port, NI_MAXSERV, "%d" , cinfo->port); |
821 | |
822 | /* set hints for getaddrinfo */ |
823 | memset(&hints, 0, sizeof(hints)); |
824 | hints.ai_protocol= IPPROTO_TCP; /* TCP connections only */ |
825 | hints.ai_family= AF_UNSPEC; /* includes: IPv4, IPv6 or hostname */ |
826 | hints.ai_socktype= SOCK_STREAM; |
827 | |
828 | /* if client has multiple interfaces, we will bind socket to given |
829 | * bind_address */ |
830 | if (cinfo->mysql->options.bind_address) |
831 | { |
832 | wait_gai= 1; |
833 | while ((gai_rc= getaddrinfo(cinfo->mysql->options.bind_address, 0, |
834 | &hints, &bind_res)) == EAI_AGAIN) |
835 | { |
836 | unsigned int timeout= mysql->options.connect_timeout ? |
837 | mysql->options.connect_timeout : DNS_TIMEOUT; |
838 | if (time(NULL) - start_t > (time_t)timeout) |
839 | break; |
840 | #ifndef _WIN32 |
841 | usleep(wait_gai); |
842 | #else |
843 | Sleep(wait_gai); |
844 | #endif |
845 | wait_gai*= 2; |
846 | } |
847 | if (gai_rc != 0 || !bind_res) |
848 | { |
849 | PVIO_SET_ERROR(cinfo->mysql, CR_BIND_ADDR_FAILED, SQLSTATE_UNKNOWN, |
850 | CER(CR_BIND_ADDR_FAILED), cinfo->mysql->options.bind_address, gai_rc); |
851 | goto error; |
852 | } |
853 | } |
854 | /* Get the address information for the server using getaddrinfo() */ |
855 | wait_gai= 1; |
856 | while ((gai_rc= getaddrinfo(cinfo->host, server_port, |
857 | &hints, &res)) == EAI_AGAIN) |
858 | { |
859 | unsigned int timeout= mysql->options.connect_timeout ? |
860 | mysql->options.connect_timeout : DNS_TIMEOUT; |
861 | if (time(NULL) - start_t > (time_t)timeout) |
862 | break; |
863 | #ifndef _WIN32 |
864 | usleep(wait_gai); |
865 | #else |
866 | Sleep(wait_gai); |
867 | #endif |
868 | wait_gai*= 2; |
869 | } |
870 | if (gai_rc != 0 || !res) |
871 | { |
872 | PVIO_SET_ERROR(cinfo->mysql, CR_UNKNOWN_HOST, SQLSTATE_UNKNOWN, |
873 | ER(CR_UNKNOWN_HOST), cinfo->host, gai_rc); |
874 | if (bind_res) |
875 | freeaddrinfo(bind_res); |
876 | goto error; |
877 | } |
878 | |
879 | /* res is a linked list of addresses for the given hostname. We loop until |
880 | we are able to connect to one address or all connect attempts failed */ |
881 | for (save_res= res; save_res; save_res= save_res->ai_next) |
882 | { |
883 | /* CONC-364: Avoid leak of open sockets */ |
884 | if (csock->socket != INVALID_SOCKET) |
885 | closesocket(csock->socket); |
886 | csock->socket= socket(save_res->ai_family, save_res->ai_socktype, |
887 | save_res->ai_protocol); |
888 | if (csock->socket == INVALID_SOCKET) |
889 | /* Errors will be handled after loop finished */ |
890 | continue; |
891 | |
892 | if (bind_res) |
893 | { |
894 | for (bres= bind_res; bres; bres= bres->ai_next) |
895 | { |
896 | if (!(rc= bind(csock->socket, bres->ai_addr, (int)bres->ai_addrlen))) |
897 | break; |
898 | } |
899 | if (rc) |
900 | { |
901 | closesocket(csock->socket); |
902 | csock->socket= INVALID_SOCKET; |
903 | continue; |
904 | } |
905 | } |
906 | |
907 | rc= pvio_socket_connect_sync_or_async(pvio, save_res->ai_addr, (uint)save_res->ai_addrlen); |
908 | if (!rc) |
909 | { |
910 | MYSQL *mysql= pvio->mysql; |
911 | if (mysql->options.extension && mysql->options.extension->async_context && |
912 | mysql->options.extension->async_context->active) |
913 | break; |
914 | if (pvio_socket_blocking(pvio, 0, 0) == SOCKET_ERROR) |
915 | { |
916 | closesocket(csock->socket); |
917 | csock->socket= INVALID_SOCKET; |
918 | continue; |
919 | } |
920 | break; /* success! */ |
921 | } |
922 | } |
923 | |
924 | freeaddrinfo(res); |
925 | if (bind_res) |
926 | freeaddrinfo(bind_res); |
927 | |
928 | if (csock->socket == INVALID_SOCKET) |
929 | { |
930 | PVIO_SET_ERROR(cinfo->mysql, CR_IPSOCK_ERROR, SQLSTATE_UNKNOWN, ER(CR_IPSOCK_ERROR), |
931 | socket_errno); |
932 | goto error; |
933 | } |
934 | |
935 | /* last call to connect 2 failed */ |
936 | if (rc) |
937 | { |
938 | PVIO_SET_ERROR(cinfo->mysql, CR_CONNECTION_ERROR, SQLSTATE_UNKNOWN, |
939 | ER(CR_CONN_HOST_ERROR), cinfo->host, |
940 | #ifdef _WIN32 |
941 | errno); |
942 | #else |
943 | socket_errno); |
944 | #endif |
945 | goto error; |
946 | } |
947 | if (pvio_socket_blocking(pvio, 1, 0) == SOCKET_ERROR) |
948 | goto error; |
949 | } |
950 | /* apply timeouts */ |
951 | if (pvio->timeout[PVIO_CONNECT_TIMEOUT] > 0) |
952 | { |
953 | if (pvio_socket_change_timeout(pvio, PVIO_READ_TIMEOUT, pvio->timeout[PVIO_CONNECT_TIMEOUT]) || |
954 | pvio_socket_change_timeout(pvio, PVIO_WRITE_TIMEOUT, pvio->timeout[PVIO_CONNECT_TIMEOUT])) |
955 | goto error; |
956 | } |
957 | else |
958 | { |
959 | if (pvio->timeout[PVIO_WRITE_TIMEOUT] > 0) |
960 | if (pvio_socket_change_timeout(pvio, PVIO_WRITE_TIMEOUT, pvio->timeout[PVIO_WRITE_TIMEOUT])) |
961 | goto error; |
962 | if (pvio->timeout[PVIO_READ_TIMEOUT] > 0) |
963 | if (pvio_socket_change_timeout(pvio, PVIO_READ_TIMEOUT, pvio->timeout[PVIO_READ_TIMEOUT])) |
964 | goto error; |
965 | } |
966 | return 0; |
967 | error: |
968 | /* close socket: MDEV-10891 */ |
969 | if (csock->socket != INVALID_SOCKET) |
970 | { |
971 | closesocket(csock->socket); |
972 | csock->socket= INVALID_SOCKET; |
973 | } |
974 | if (pvio->data) |
975 | { |
976 | free((gptr)pvio->data); |
977 | pvio->data= NULL; |
978 | } |
979 | return 1; |
980 | } |
981 | |
982 | /* {{{ my_bool pvio_socket_close() */ |
983 | my_bool pvio_socket_close(MARIADB_PVIO *pvio) |
984 | { |
985 | struct st_pvio_socket *csock= NULL; |
986 | int r= 0; |
987 | |
988 | if (!pvio) |
989 | return 1; |
990 | |
991 | if (pvio->data) |
992 | { |
993 | csock= (struct st_pvio_socket *)pvio->data; |
994 | if (csock && csock->socket != INVALID_SOCKET) |
995 | { |
996 | r= closesocket(csock->socket); |
997 | csock->socket= INVALID_SOCKET; |
998 | } |
999 | free((gptr)pvio->data); |
1000 | pvio->data= NULL; |
1001 | } |
1002 | return r; |
1003 | } |
1004 | /* }}} */ |
1005 | |
1006 | /* {{{ my_socket pvio_socket_get_handle */ |
1007 | my_bool pvio_socket_get_handle(MARIADB_PVIO *pvio, void *handle) |
1008 | { |
1009 | if (pvio && pvio->data && handle) |
1010 | { |
1011 | *(my_socket *)handle= ((struct st_pvio_socket *)pvio->data)->socket; |
1012 | return 0; |
1013 | } |
1014 | return 1; |
1015 | } |
1016 | /* }}} */ |
1017 | |
1018 | /* {{{ my_bool pvio_socket_is_blocking(MARIADB_PVIO *pvio) */ |
1019 | my_bool pvio_socket_is_blocking(MARIADB_PVIO *pvio) |
1020 | { |
1021 | struct st_pvio_socket *csock= NULL; |
1022 | my_bool r; |
1023 | |
1024 | if (!pvio || !pvio->data) |
1025 | return 0; |
1026 | |
1027 | csock= (struct st_pvio_socket *)pvio->data; |
1028 | r = !(csock->fcntl_mode & O_NONBLOCK); |
1029 | return r; |
1030 | } |
1031 | /* }}} */ |
1032 | |
1033 | /* {{{ my_bool pvio_socket_is_alive(MARIADB_PVIO *pvio) */ |
1034 | my_bool pvio_socket_is_alive(MARIADB_PVIO *pvio) |
1035 | { |
1036 | struct st_pvio_socket *csock= NULL; |
1037 | #ifndef _WIN32 |
1038 | struct pollfd poll_fd; |
1039 | #else |
1040 | FD_SET sfds; |
1041 | struct timeval tv= {0,0}; |
1042 | #endif |
1043 | int res; |
1044 | |
1045 | if (!pvio || !pvio->data) |
1046 | return 0; |
1047 | |
1048 | csock= (struct st_pvio_socket *)pvio->data; |
1049 | #ifndef _WIN32 |
1050 | memset(&poll_fd, 0, sizeof(struct pollfd)); |
1051 | poll_fd.events= POLLPRI | POLLIN; |
1052 | poll_fd.fd= csock->socket; |
1053 | |
1054 | res= poll(&poll_fd, 1, 0); |
1055 | if (res <= 0) /* timeout or error */ |
1056 | return FALSE; |
1057 | if (!(poll_fd.revents & (POLLIN | POLLPRI))) |
1058 | return FALSE; |
1059 | return TRUE; |
1060 | #else |
1061 | /* We can't use the WSAPoll function, it's broken :-( |
1062 | (see Windows 8 Bugs 309411 - WSAPoll does not report failed connections) |
1063 | Instead we need to use select function: |
1064 | If TIMEVAL is initialized to {0, 0}, select will return immediately; |
1065 | this is used to poll the state of the selected sockets. |
1066 | */ |
1067 | FD_ZERO(&sfds); |
1068 | FD_SET(csock->socket, &sfds); |
1069 | |
1070 | res= select((int)csock->socket + 1, &sfds, NULL, NULL, &tv); |
1071 | if (res > 0 && FD_ISSET(csock->socket, &sfds)) |
1072 | return TRUE; |
1073 | return FALSE; |
1074 | #endif |
1075 | } |
1076 | /* }}} */ |
1077 | |
1078 | /* {{{ my_boool pvio_socket_has_data */ |
1079 | my_bool pvio_socket_has_data(MARIADB_PVIO *pvio, ssize_t *data_len) |
1080 | { |
1081 | struct st_pvio_socket *csock= NULL; |
1082 | char tmp_buf; |
1083 | ssize_t len; |
1084 | my_bool mode; |
1085 | |
1086 | if (!pvio || !pvio->data) |
1087 | return 0; |
1088 | |
1089 | csock= (struct st_pvio_socket *)pvio->data; |
1090 | /* MSG_PEEK: Peeks at the incoming data. The data is copied into the buffer, |
1091 | but is not removed from the input queue. |
1092 | */ |
1093 | pvio_socket_blocking(pvio, 0, &mode); |
1094 | len= recv(csock->socket, &tmp_buf, sizeof(tmp_buf), MSG_PEEK); |
1095 | pvio_socket_blocking(pvio, mode, 0); |
1096 | if (len < 0) |
1097 | return 1; |
1098 | *data_len= len; |
1099 | return 0; |
1100 | } |
1101 | /* }}} */ |
1102 | |
1103 | int pvio_socket_shutdown(MARIADB_PVIO *pvio) |
1104 | { |
1105 | if (pvio && pvio->data) |
1106 | { |
1107 | my_socket s = ((struct st_pvio_socket *)pvio->data)->socket; |
1108 | #ifdef _WIN32 |
1109 | shutdown(s, SD_BOTH); |
1110 | CancelIoEx((HANDLE)s, NULL); |
1111 | #else |
1112 | shutdown(s, SHUT_RDWR); |
1113 | #endif |
1114 | } |
1115 | return -1; |
1116 | } |
1117 | |