Update from 1.0.0-stable.
[openssl.git] / crypto / bio / bss_dgram.c
index 173d871f567d9f81581942cff0483ad6402efa87..80d31b199de984076985e072072594780a5031c2 100644 (file)
@@ -70,7 +70,9 @@
 #include <sys/timeb.h>
 #endif
 
+#ifdef OPENSSL_SYS_LINUX
 #define IP_MTU      14 /* linux is lame */
+#endif
 
 #ifdef WATT32
 #define sock_write SockWrite  /* Watt-32 uses same names */
@@ -287,7 +289,6 @@ static int dgram_read(BIO *b, char *out, int outl)
                                BIO_set_retry_read(b);
                                data->_errno = get_last_socket_error();
                                }
-                       memset(&(data->hstimeout), 0, sizeof(struct timeval));
                        }
                }
        return(ret);
@@ -334,6 +335,10 @@ static long dgram_ctrl(BIO *b, int cmd, long num, void *ptr)
        bio_dgram_data *data = NULL;
        long sockopt_val = 0;
        unsigned int sockopt_len = 0;
+#ifdef OPENSSL_SYS_LINUX
+       socklen_t addr_len;
+       struct sockaddr_storage addr;
+#endif
 
        data = (bio_dgram_data *)b->ptr;
 
@@ -392,24 +397,87 @@ static long dgram_ctrl(BIO *b, int cmd, long num, void *ptr)
 #endif
                break;
                /* (Linux)kernel sets DF bit on outgoing IP packets */
-#ifdef IP_MTU_DISCOVER
        case BIO_CTRL_DGRAM_MTU_DISCOVER:
-               sockopt_val = IP_PMTUDISC_DO;
-               if ((ret = setsockopt(b->num, IPPROTO_IP, IP_MTU_DISCOVER,
-                       &sockopt_val, sizeof(sockopt_val))) < 0)
-                       perror("setsockopt");
+#ifdef OPENSSL_SYS_LINUX
+               addr_len = (socklen_t)sizeof(struct sockaddr_storage);
+               memset((void *)&addr, 0, sizeof(struct sockaddr_storage));
+               if (getsockname(b->num, (void *)&addr, &addr_len) < 0)
+                       {
+                       ret = 0;
+                       break;
+                       }
+               sockopt_len = sizeof(sockopt_val);
+               switch (addr.ss_family)
+                       {
+               case AF_INET:
+                       sockopt_val = IP_PMTUDISC_DO;
+                       if ((ret = setsockopt(b->num, IPPROTO_IP, IP_MTU_DISCOVER,
+                               &sockopt_val, sizeof(sockopt_val))) < 0)
+                               perror("setsockopt");
+                       break;
+               case AF_INET6:
+                       sockopt_val = IPV6_PMTUDISC_DO;
+                       if ((ret = setsockopt(b->num, IPPROTO_IPV6, IPV6_MTU_DISCOVER,
+                               &sockopt_val, sizeof(sockopt_val))) < 0)
+                               perror("setsockopt");
+                       break;
+               default:
+                       ret = -1;
+                       break;
+                       }
+               ret = -1;
+#else
                break;
 #endif
        case BIO_CTRL_DGRAM_QUERY_MTU:
-         sockopt_len = sizeof(sockopt_val);
-               if ((ret = getsockopt(b->num, IPPROTO_IP, IP_MTU, (void *)&sockopt_val,
-                       &sockopt_len)) < 0 || sockopt_val < 0)
-                       { ret = 0; }
-               else
+#ifdef OPENSSL_SYS_LINUX
+               addr_len = (socklen_t)sizeof(struct sockaddr_storage);
+               memset((void *)&addr, 0, sizeof(struct sockaddr_storage));
+               if (getsockname(b->num, (void *)&addr, &addr_len) < 0)
                        {
-                       data->mtu = sockopt_val;
-                       ret = data->mtu;
+                       ret = 0;
+                       break;
                        }
+               sockopt_len = sizeof(sockopt_val);
+               switch (addr.ss_family)
+                       {
+               case AF_INET:
+                       if ((ret = getsockopt(b->num, IPPROTO_IP, IP_MTU, (void *)&sockopt_val,
+                               &sockopt_len)) < 0 || sockopt_val < 0)
+                               {
+                               ret = 0;
+                               }
+                       else
+                               {
+                               /* we assume that the transport protocol is UDP and no
+                                * IP options are used.
+                                */
+                               data->mtu = sockopt_val - 8 - 20;
+                               ret = data->mtu;
+                               }
+                       break;
+               case AF_INET6:
+                       if ((ret = getsockopt(b->num, IPPROTO_IPV6, IPV6_MTU, (void *)&sockopt_val,
+                               &sockopt_len)) < 0 || sockopt_val < 0)
+                               {
+                               ret = 0;
+                               }
+                       else
+                               {
+                               /* we assume that the transport protocol is UDP and no
+                                * IPV6 options are used.
+                                */
+                               data->mtu = sockopt_val - 8 - 40;
+                               ret = data->mtu;
+                               }
+                       break;
+               default:
+                       ret = 0;
+                       break;
+                       }
+#else
+               ret = 0;
+#endif
                break;
        case BIO_CTRL_DGRAM_GET_MTU:
                return data->mtu;