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