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