summaryrefslogtreecommitdiff
path: root/mail/exim-old/files/patch-src::daemon.c
blob: 72ee5952dd6f6613e68f31a5d24b492f7d789562 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
--- src/daemon.c.orig	Tue Jul 10 11:52:32 2001
+++ src/daemon.c	Tue Jul 10 11:53:33 2001
@@ -661,7 +661,7 @@
   union sockaddr_46 sin;
 
   #if HAVE_IPV6
-  struct in6_addr anyaddr6 = IN6ADDR_ANY_INIT;
+  struct in6_addr anyaddr6 = in6addr_any;
   #endif
 
   /* Find the standard SMTP port if no port number given; otherwise
@@ -690,7 +690,11 @@
     }
 
   /* Otherwise set up one address item with a null address, implying listening
-  on all interfaces. */
+  on all interfaces. In an IPv6 world, we set up a second address for listening
+  on all IPv6 interfaces. Some IPv6 stacks will pick up incoming IPv4 calls on
+  an IPv6 wildcard socket, but some won't (there are security issues). Using
+  two sockets should work in all cases. We identify an IPv6 wildcard address by
+  the string ":". */
 
   else
     {
@@ -698,6 +702,13 @@
     addresses->next = NULL;
     addresses->address[0] = 0;
     listen_socket_count = 1;
+    #if HAVE_IPV6
+    addresses->next = store_get(sizeof(ip_address_item));
+    addresses->next->next = NULL;
+    addresses->next->address[0] = ':';
+    addresses->next->address[1] = 0;
+    listen_socket_count++;
+    #endif  /* HAVE_IPV6 */
     }
 
   /* Get a vector to remember all the sockets in */
@@ -714,41 +725,20 @@
   #endif
 
   /* For each IP address, create a socket and bind it to the appropriate
-  port. Note that if local_interfaces is not set, we bind a single socket to
-  all interfaces. */
+  port. Some IPv6 stacks can handle IPv4 addresses on IPv6 sockets using
+  the mapping facilities. However, some don't do this because of security
+  concerns. Therefore, we use IPv4 sockets for IPv4 addresses even in an
+  IPv6 world. */
 
   for (ipa = addresses, sk = 0; sk < listen_socket_count; ipa = ipa->next, sk++)
     {
     int i;
+    int af = (strchr(ipa->address, ':') != NULL)? AF_INET6 : AF_INET;
 
-    #if HAVE_IPV6
-    BOOL ipv6_socket = TRUE;
-    BOOL ipv6_address = strchr(ipa->address, ':') != NULL;
-
-    /* A system which has all the IPv6 libraries etc. may nevertheless not
-    support IPv6 in the kernel, so if the socket creation fails, fall back
-    on creating an IPv4 socket, except when we have an explicit IPv6 address,
-    for which we really do need an IPv6 socket. */
-
-    listen_sockets[sk] = socket(AF_INET6, SOCK_STREAM, 0);
-    if (listen_sockets[sk] < 0)
-      {
-      if (!ipv6_address)
-        {
-        listen_sockets[sk] = socket(AF_INET, SOCK_STREAM, 0);
-        ipv6_socket = FALSE;
-        }
-      }
-
-    /* The IPv4 case */
-
-    #else    /* HAVE_IPV6 */
-    listen_sockets[sk] = socket(AF_INET, SOCK_STREAM, 0);
-    #endif   /* HAVE_IPV6 */
-
+    listen_sockets[sk] = socket(af, SOCK_STREAM, 0);
     if (listen_sockets[sk] < 0)
-      log_write(0, LOG_PANIC_DIE, "socket creation failed: %s",
-        strerror(errno));
+      log_write(0, LOG_PANIC_DIE, "IPv%c socket creation failed: %s",
+        (af == AF_INET6)? '6' : '4', strerror(errno));
 
     /* Set SO_REUSEADDR so that the daemon can be restarted while a connection
     is being handled.  Without this, a connection will prevent reuse of the
@@ -765,44 +755,29 @@
 
     memset(&sin, 0, sizeof(sin));
 
-    /* This code has to be entirely different for IPv6 and IPv4 because
-    of the different struct layouts. Sigh. Under IPv6, however, we may have
-    ended up with an IPv4 socket because the kernel may not support IPv6 even
-    if the libraries etc. are available. In this case, revert to IPv4 style
-    code (this will happen only when the the address is not an IPv6 one).
-    Although the code is identical, I've chosen to replicate it for ease of
-    reading the two cases. */
+    /* Setup code when using an IPv6 socket. The wildcard address is ":", to
+    ensure an IPv6 socket is used. */
 
     #if HAVE_IPV6
-    if (ipv6_socket)
+    if (af == AF_INET6)
       {
       sin.v6.sin6_family = AF_INET6;
       sin.v6.sin6_port = net_port;
-
-      if (ipa->address[0] == 0)
+      if (ipa->address[0] == ':' && ipa->address[1] == 0)
         {
         sin.v6.sin6_addr = anyaddr6;
         DEBUG(9) debug_printf("listening on all interfaces (IPv6)\n");
         }
       else
         {
-        int rc;
-        if (ipv6_address)
-          rc = inet_pton(AF_INET6, ipa->address, &sin.v6.sin6_addr);
-        else
-        /* We have an IPv6 socket, and an IPv4 address. We have to turn the
-        address into an IPv6 mapped form. */
-          {
-          memmove(ipa->address+7, ipa->address, (int)strlen(ipa->address) + 1);
-          memcpy(ipa->address, "::ffff:", 7);
-          rc = inet_pton(AF_INET6, ipa->address, &sin.v6.sin6_addr);
-          }
-        if (rc != 1)
+        if (inet_pton(AF_INET6, ipa->address, &sin.v6.sin6_addr) != 1)
           log_write(0, LOG_PANIC_DIE, "unable to parse \"%s\"", ipa->address);
         DEBUG(9) debug_printf("listening on %s\n", ipa->address);
         }
       }
-    else   /* Revert to IPv4 code */
+    else
+    #endif  /* HAVE_IPV6 */
+    /* Setup code when using IPv4 socket. The wildcard address is "". */
       {
       sin.v4.sin_family = AF_INET;
       sin.v4.sin_port = net_port;
@@ -817,34 +792,25 @@
         DEBUG(9) debug_printf("listening on %s\n", ipa->address);
         }
       }
-    #else   /* HAVE_IPV6 */
 
-
-    /* The IPv4 case is simple. */
-
-    sin.v4.sin_family = AF_INET;
-    sin.v4.sin_port = net_port;
-    if (ipa->address[0] == 0)
-      {
-      sin.v4.sin_addr.s_addr = (S_ADDR_TYPE)INADDR_ANY;
-      DEBUG(9) debug_printf("listening on all interfaces\n");
-      }
-    else
-      {
-      sin.v4.sin_addr.s_addr = (S_ADDR_TYPE)inet_addr(ipa->address);
-      DEBUG(9) debug_printf("listening on %s\n", ipa->address);
-      }
-    #endif  /* HAVE_IPV6 */
-
-
-    /* Retry loop */
+    /* Retry loop. Need to get the length of sin right for IPv4/IPv6
+    for some operating systems that check it. Although AF_INET6 is always
+    available (even on old IPv4 systems), the sin.v6 field isn't. */
 
     for (i = 9; i >= 0; i--)
       {
-      if (bind(listen_sockets[sk], (struct sockaddr *)&sin, sizeof(sin)) < 0)
+      if (bind(listen_sockets[sk], (struct sockaddr *)&sin,
+               #if HAVE_IPV6
+               (af == AF_INET6)? sizeof(sin.v6) : sizeof(sin.v4)
+               #else
+               sizeof(sin)
+               #endif /* HAVE_IPV6 */
+           ) < 0)
         {
         char *msg = strerror(errno);
-        char *addr = (ipa->address[0] == 0)? "(any)" : ipa->address;
+        char *addr = (ipa->address[0] == 0)? "(any IPv4)" :
+          (ipa->address[0] == ':' && ipa->address[1] == 0)? "(any IPv6)" :
+          ipa->address;
         if (i == 0)
           log_write(0, LOG_MAIN|LOG_PANIC_DIE,
             "socket bind() to port %d for address %s failed: %s: "