Commit | Line | Data |
---|---|---|
f1e894f3 | 1 | /* $Cambridge: exim/src/src/ip.c,v 1.3 2005/06/27 14:29:43 ph10 Exp $ */ |
059ec3d9 PH |
2 | |
3 | /************************************************* | |
4 | * Exim - an Internet mail transport agent * | |
5 | *************************************************/ | |
6 | ||
c988f1f4 | 7 | /* Copyright (c) University of Cambridge 1995 - 2005 */ |
059ec3d9 PH |
8 | /* See the file NOTICE for conditions of use and distribution. */ |
9 | ||
10 | /* Functions for doing things with sockets. With the advent of IPv6 this has | |
11 | got messier, so that it's worth pulling out the code into separate functions | |
12 | that other parts of Exim can call, expecially as there are now several | |
13 | different places in the code where sockets are used. */ | |
14 | ||
15 | ||
16 | #include "exim.h" | |
17 | ||
18 | ||
19 | /************************************************* | |
20 | * Create a socket * | |
21 | *************************************************/ | |
22 | ||
23 | /* Socket creation happens in a number of places so it's packaged here for | |
24 | convenience. | |
25 | ||
26 | Arguments: | |
27 | type SOCK_DGRAM or SOCK_STREAM | |
28 | af AF_INET or AF_INET6 | |
29 | ||
30 | Returns: socket number or -1 on failure | |
31 | */ | |
32 | ||
33 | int | |
34 | ip_socket(int type, int af) | |
35 | { | |
36 | int sock = socket(af, type, 0); | |
37 | if (sock < 0) | |
38 | log_write(0, LOG_MAIN, "IPv%c socket creation failed: %s", | |
39 | (af == AF_INET6)? '6':'4', strerror(errno)); | |
40 | return sock; | |
41 | } | |
42 | ||
43 | ||
44 | ||
45 | ||
46 | #if HAVE_IPV6 | |
47 | /************************************************* | |
48 | * Convert printing address to numeric * | |
49 | *************************************************/ | |
50 | ||
51 | /* This function converts the textual form of an IP address into a numeric form | |
52 | in an appropriate structure in an IPv6 environment. The getaddrinfo() function | |
53 | can (apparently) handle more complicated addresses (e.g. those containing | |
54 | scopes) than inet_pton() in some environments. We use hints to tell it that the | |
55 | input must be a numeric address. | |
56 | ||
57 | However, apparently some operating systems (or libraries) don't support | |
58 | getaddrinfo(), so there is a build-time option to revert to inet_pton() (which | |
59 | does not support scopes). | |
60 | ||
61 | Arguments: | |
62 | address textual form of the address | |
63 | addr where to copy back the answer | |
64 | ||
65 | Returns: nothing - failure provokes a panic-die | |
66 | */ | |
67 | ||
68 | static void | |
69 | ip_addrinfo(uschar *address, struct sockaddr_in6 *saddr) | |
70 | { | |
71 | #ifdef IPV6_USE_INET_PTON | |
72 | ||
73 | if (inet_pton(AF_INET6, CS address, &saddr->sin6_addr) != 1) | |
74 | log_write(0, LOG_MAIN|LOG_PANIC_DIE, "unable to parse \"%s\" as an " | |
75 | "IP address", address); | |
76 | saddr->sin6_family = AF_INET6; | |
77 | ||
78 | #else | |
79 | ||
80 | int rc; | |
81 | struct addrinfo hints, *res; | |
82 | memset(&hints, 0, sizeof(hints)); | |
83 | hints.ai_family = AF_INET6; | |
84 | hints.ai_socktype = SOCK_STREAM; | |
85 | hints.ai_flags = AI_NUMERICHOST; | |
86 | if ((rc = getaddrinfo(CS address, NULL, &hints, &res)) != 0 || res == NULL) | |
87 | log_write(0, LOG_MAIN|LOG_PANIC_DIE, "unable to parse \"%s\" as an " | |
88 | "IP address: %s", address, | |
89 | (rc == 0)? "NULL result returned" : gai_strerror(rc)); | |
90 | memcpy(saddr, res->ai_addr, res->ai_addrlen); | |
91 | freeaddrinfo(res); | |
92 | ||
93 | #endif | |
94 | } | |
95 | #endif /* HAVE_IPV6 */ | |
96 | ||
97 | ||
98 | /************************************************* | |
99 | * Bind socket to interface and port * | |
100 | *************************************************/ | |
101 | ||
102 | /* This function binds a socket to a local interface address and port. For a | |
103 | wildcard IPv6 bind, the address is ":". | |
104 | ||
105 | Arguments: | |
106 | sock the socket | |
107 | af AF_INET or AF_INET6 - the socket type | |
108 | address the IP address, in text form | |
109 | port the IP port (host order) | |
110 | ||
111 | Returns: the result of bind() | |
112 | */ | |
113 | ||
114 | int | |
115 | ip_bind(int sock, int af, uschar *address, int port) | |
116 | { | |
117 | int s_len; | |
118 | union sockaddr_46 sin; | |
119 | memset(&sin, 0, sizeof(sin)); | |
120 | ||
121 | /* Setup code when using an IPv6 socket. The wildcard address is ":", to | |
122 | ensure an IPv6 socket is used. */ | |
123 | ||
124 | #if HAVE_IPV6 | |
125 | if (af == AF_INET6) | |
126 | { | |
127 | if (address[0] == ':' && address[1] == 0) | |
128 | { | |
129 | sin.v6.sin6_family = AF_INET6; | |
130 | sin.v6.sin6_addr = in6addr_any; | |
131 | } | |
132 | else | |
133 | { | |
134 | ip_addrinfo(address, &sin.v6); /* Panic-dies on error */ | |
135 | } | |
136 | sin.v6.sin6_port = htons(port); | |
137 | s_len = sizeof(sin.v6); | |
138 | } | |
139 | else | |
140 | #else /* HAVE_IPv6 */ | |
141 | af = af; /* Avoid compiler warning */ | |
142 | #endif /* HAVE_IPV6 */ | |
143 | ||
144 | /* Setup code when using IPv4 socket. The wildcard address is "". */ | |
145 | ||
146 | { | |
147 | sin.v4.sin_family = AF_INET; | |
148 | sin.v4.sin_port = htons(port); | |
149 | s_len = sizeof(sin.v4); | |
150 | if (address[0] == 0) | |
151 | sin.v4.sin_addr.s_addr = (S_ADDR_TYPE)INADDR_ANY; | |
152 | else | |
153 | sin.v4.sin_addr.s_addr = (S_ADDR_TYPE)inet_addr(CS address); | |
154 | } | |
155 | ||
156 | /* Now we can call the bind() function */ | |
157 | ||
158 | return bind(sock, (struct sockaddr *)&sin, s_len); | |
159 | } | |
160 | ||
161 | ||
162 | ||
163 | /************************************************* | |
164 | * Connect socket to remote host * | |
165 | *************************************************/ | |
166 | ||
167 | /* This function connects a socket to a remote address and port. The socket may | |
168 | or may not have previously been bound to a local interface. | |
169 | ||
170 | Arguments: | |
171 | sock the socket | |
172 | af AF_INET6 or AF_INET for the socket type | |
173 | address the remote address, in text form | |
174 | port the remote port | |
175 | timeout a timeout | |
176 | ||
177 | Returns: 0 on success; -1 on failure, with errno set | |
178 | */ | |
179 | ||
180 | int | |
181 | ip_connect(int sock, int af, uschar *address, int port, int timeout) | |
182 | { | |
183 | struct sockaddr_in s_in4; | |
184 | struct sockaddr *s_ptr; | |
185 | int s_len, rc, save_errno; | |
186 | ||
187 | /* For an IPv6 address, use an IPv6 sockaddr structure. */ | |
188 | ||
189 | #if HAVE_IPV6 | |
190 | struct sockaddr_in6 s_in6; | |
191 | if (af == AF_INET6) | |
192 | { | |
193 | memset(&s_in6, 0, sizeof(s_in6)); | |
194 | ip_addrinfo(address, &s_in6); /* Panic-dies on error */ | |
195 | s_in6.sin6_port = htons(port); | |
196 | s_ptr = (struct sockaddr *)&s_in6; | |
197 | s_len = sizeof(s_in6); | |
198 | } | |
199 | else | |
200 | #else /* HAVE_IPV6 */ | |
201 | af = af; /* Avoid compiler warning */ | |
202 | #endif /* HAVE_IPV6 */ | |
203 | ||
204 | /* For an IPv4 address, use an IPv4 sockaddr structure, even on a system with | |
205 | IPv6 support. */ | |
206 | ||
207 | { | |
208 | memset(&s_in4, 0, sizeof(s_in4)); | |
209 | s_in4.sin_family = AF_INET; | |
210 | s_in4.sin_port = htons(port); | |
211 | s_in4.sin_addr.s_addr = (S_ADDR_TYPE)inet_addr(CS address); | |
212 | s_ptr = (struct sockaddr *)&s_in4; | |
213 | s_len = sizeof(s_in4); | |
214 | } | |
215 | ||
216 | /* If no connection timeout is set, just call connect() without setting a | |
217 | timer, thereby allowing the inbuilt OS timeout to operate. */ | |
218 | ||
219 | sigalrm_seen = FALSE; | |
220 | if (timeout > 0) alarm(timeout); | |
221 | rc = connect(sock, s_ptr, s_len); | |
222 | save_errno = errno; | |
223 | alarm(0); | |
224 | ||
225 | /* There is a testing facility for simulating a connection timeout, as I | |
226 | can't think of any other way of doing this. It converts a connection refused | |
227 | into a timeout. | |
228 | ||
229 | I had to add a second fudge to keep the tests working. Attempts to connect to | |
230 | 10.x.x.x are expected to timeout, but sometimes they now give "No route to | |
231 | host". */ | |
232 | ||
233 | if (running_in_test_harness) | |
234 | { | |
235 | if ((save_errno == ECONNREFUSED && timeout == 999999) || | |
236 | (save_errno == EHOSTUNREACH && timeout > 0 && | |
237 | Ustrncmp(address, "10.", 3) == 0)) | |
238 | { | |
239 | rc = -1; | |
240 | save_errno = EINTR; | |
241 | sigalrm_seen = TRUE; | |
242 | } | |
243 | } | |
244 | ||
245 | /* Success */ | |
246 | ||
247 | if (rc >= 0) return 0; | |
248 | ||
249 | /* A failure whose error code is "Interrupted system call" is in fact | |
250 | an externally applied timeout if the signal handler has been run. */ | |
251 | ||
f1e894f3 | 252 | (void)close(sock); |
059ec3d9 PH |
253 | errno = (save_errno == EINTR && sigalrm_seen)? ETIMEDOUT : save_errno; |
254 | return -1; | |
255 | } | |
256 | ||
257 | ||
258 | ||
259 | /************************************************* | |
260 | * Set keepalive on a socket * | |
261 | *************************************************/ | |
262 | ||
263 | /* Can be called for both incoming and outgoing sockets. | |
264 | ||
265 | Arguments: | |
266 | sock the socket | |
267 | address the remote host address, for failure logging | |
268 | torf true for outgoing connection, false for incoming | |
269 | ||
270 | Returns: nothing | |
271 | */ | |
272 | ||
273 | void | |
274 | ip_keepalive(int sock, uschar *address, BOOL torf) | |
275 | { | |
276 | int fodder = 1; | |
277 | if (setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, | |
278 | (uschar *)(&fodder), sizeof(fodder)) != 0) | |
279 | log_write(0, LOG_MAIN, "setsockopt(SO_KEEPALIVE) on connection %s %s " | |
280 | "failed: %s", torf? "to":"from", address, strerror(errno)); | |
281 | } | |
282 | ||
283 | ||
284 | ||
285 | /************************************************* | |
286 | * Receive from a socket with timeout * | |
287 | *************************************************/ | |
288 | ||
289 | /* The timeout is implemented using select(), and we loop to cover select() | |
290 | getting interrupted, and the possibility of select() returning with a positive | |
291 | result but no ready descriptor. Is this in fact possible? | |
292 | ||
293 | Arguments: | |
294 | sock the socket | |
295 | buffer to read into | |
296 | bufsize the buffer size | |
297 | timeout the timeout | |
298 | ||
299 | Returns: > 0 => that much data read | |
300 | <= 0 on error or EOF; errno set - zero for EOF | |
301 | */ | |
302 | ||
303 | int | |
304 | ip_recv(int sock, uschar *buffer, int buffsize, int timeout) | |
305 | { | |
306 | fd_set select_inset; | |
307 | struct timeval tv; | |
308 | int start_recv = time(NULL); | |
309 | int rc; | |
310 | ||
311 | /* Wait until the socket is ready */ | |
312 | ||
313 | for (;;) | |
314 | { | |
315 | FD_ZERO (&select_inset); | |
316 | FD_SET (sock, &select_inset); | |
317 | tv.tv_sec = timeout; | |
318 | tv.tv_usec = 0; | |
319 | ||
320 | DEBUG(D_transport) debug_printf("waiting for data on socket\n"); | |
321 | rc = select(sock + 1, (SELECT_ARG2_TYPE *)&select_inset, NULL, NULL, &tv); | |
322 | ||
323 | /* If some interrupt arrived, just retry. We presume this to be rare, | |
324 | but it can happen (e.g. the SIGUSR1 signal sent by exiwhat causes | |
325 | select() to exit). | |
326 | ||
327 | Aug 2004: Somebody set up a cron job that ran exiwhat every 2 minutes, making | |
328 | the interrupt not at all rare. Since the timeout is typically more than 2 | |
329 | minutes, the effect was to block the timeout completely. To prevent this | |
330 | happening again, we do an explicit time test. */ | |
331 | ||
332 | if (rc < 0 && errno == EINTR) | |
333 | { | |
334 | DEBUG(D_transport) debug_printf("EINTR while waiting for socket data\n"); | |
335 | if (time(NULL) - start_recv < timeout) continue; | |
336 | DEBUG(D_transport) debug_printf("total wait time exceeds timeout\n"); | |
337 | } | |
338 | ||
339 | /* Handle a timeout, and treat any other select error as a timeout, including | |
340 | an EINTR when we have been in this loop for longer than timeout. */ | |
341 | ||
342 | if (rc <= 0) | |
343 | { | |
344 | errno = ETIMEDOUT; | |
345 | return -1; | |
346 | } | |
347 | ||
348 | /* If the socket is ready, break out of the loop. */ | |
349 | ||
350 | if (FD_ISSET(sock, &select_inset)) break; | |
351 | } | |
352 | ||
353 | /* The socket is ready, read from it (via TLS if it's active). On EOF (i.e. | |
354 | close down of the connection), set errno to zero; otherwise leave it alone. */ | |
355 | ||
356 | #ifdef SUPPORT_TLS | |
357 | if (tls_active == sock) | |
358 | rc = tls_read(buffer, buffsize); | |
359 | else | |
360 | #endif | |
361 | rc = recv(sock, buffer, buffsize, 0); | |
362 | ||
363 | if (rc > 0) return rc; | |
364 | if (rc == 0) errno = 0; | |
365 | return -1; | |
366 | } | |
367 | ||
368 | ||
369 | /* End of ip.c */ |