1 /*
2 * libEtPan! -- a mail stuff library
3 *
4 * Copyright (C) 2001, 2005 - DINH Viet Hoa
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the libEtPan! project nor the names of its
16 * contributors may be used to endorse or promote products derived
17 * from this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32 /*
33 * $Id: mailstream_socket.c,v 1.33 2011/04/15 10:43:31 hoa Exp $
34 */
35
36 #ifdef HAVE_CONFIG_H
37 # include <config.h>
38 #endif
39
40 #include "mailstream_socket.h"
41
42 #ifdef HAVE_UNISTD_H
43 # include <unistd.h>
44 #endif
45 #include <stdlib.h>
46 #include <fcntl.h>
47 #ifdef HAVE_SYS_SOCKET_H
48 # include <sys/socket.h>
49 #endif
50
51 #ifdef HAVE_STRING_H
52 # include <string.h>
53 #endif
54
55 /*
56 these 3 headers MUST be included before <sys/select.h>
57 to insure compatibility with Mac OS X (this is true for 10.2)
58 */
59
60 #ifdef WIN32
61 # include <win_etpan.h>
62 #else
63 # include <sys/time.h>
64 # include <sys/types.h>
65 # if USE_POLL
66 # ifdef HAVE_SYS_POLL_H
67 # include <sys/poll.h>
68 # endif
69 # else
70 # ifdef HAVE_SYS_SELECT_H
71 # include <sys/select.h>
72 # endif
73 # endif
74 #endif
75
76 #include "mailstream_cancel.h"
77
78 struct mailstream_socket_data {
79 int fd;
80 struct mailstream_cancel * cancel;
81 int use_read;
82 };
83
84 /* mailstream_low, socket */
85
86 static int mailstream_low_socket_close(mailstream_low * s);
87 static ssize_t mailstream_low_socket_read(mailstream_low * s,
88 void * buf, size_t count);
89 static ssize_t mailstream_low_socket_write(mailstream_low * s,
90 const void * buf, size_t count);
91 static void mailstream_low_socket_free(mailstream_low * s);
92 static int mailstream_low_socket_get_fd(mailstream_low * s);
93 static void mailstream_low_socket_cancel(mailstream_low * s);
94 static struct mailstream_cancel * mailstream_low_socket_get_cancel(mailstream_low * s);
95
96 static mailstream_low_driver local_mailstream_socket_driver = {
97 /* mailstream_read */ mailstream_low_socket_read,
98 /* mailstream_write */ mailstream_low_socket_write,
99 /* mailstream_close */ mailstream_low_socket_close,
100 /* mailstream_get_fd */ mailstream_low_socket_get_fd,
101 /* mailstream_free */ mailstream_low_socket_free,
102 /* mailstream_cancel */ mailstream_low_socket_cancel,
103 /* mailstream_get_cancel */ mailstream_low_socket_get_cancel,
104 /* mailstream_get_certificate_chain */ NULL,
105 /* mailstream_setup_idle */ NULL,
106 /* mailstream_unsetup_idle */ NULL,
107 /* mailstream_interrupt_idle */ NULL,
108 };
109
110 mailstream_low_driver * mailstream_socket_driver =
111 &local_mailstream_socket_driver;
112
113 /* file descriptor must be given in (default) blocking-mode */
114
socket_data_new(int fd)115 static struct mailstream_socket_data * socket_data_new(int fd)
116 {
117 struct mailstream_socket_data * socket_data;
118
119 socket_data = (struct mailstream_socket_data * ) malloc(sizeof(* socket_data));
120 if (socket_data == NULL)
121 goto err;
122
123 socket_data->fd = fd;
124 socket_data->use_read = 0;
125 socket_data->cancel = mailstream_cancel_new();
126 if (socket_data->cancel == NULL)
127 goto free;
128
129 return socket_data;
130
131 free:
132 free(socket_data);
133 err:
134 return NULL;
135 }
136
socket_data_free(struct mailstream_socket_data * socket_data)137 static void socket_data_free(struct mailstream_socket_data * socket_data)
138 {
139 mailstream_cancel_free(socket_data->cancel);
140 free(socket_data);
141 }
142
socket_data_close(struct mailstream_socket_data * socket_data)143 static void socket_data_close(struct mailstream_socket_data * socket_data)
144 {
145 #ifdef WIN32
146 /* SEB */
147 closesocket(socket_data->fd);
148 #else
149 close(socket_data->fd);
150 #endif
151 socket_data->fd = -1;
152 }
153
mailstream_low_socket_open(int fd)154 mailstream_low * mailstream_low_socket_open(int fd)
155 {
156 mailstream_low * s;
157 struct mailstream_socket_data * socket_data;
158
159 socket_data = socket_data_new(fd);
160 if (socket_data == NULL)
161 goto err;
162
163 s = mailstream_low_new(socket_data, mailstream_socket_driver);
164 if (s == NULL)
165 goto free_socket_data;
166
167 return s;
168
169 free_socket_data:
170 socket_data_free(socket_data);
171 err:
172 return NULL;
173 }
174
mailstream_low_socket_close(mailstream_low * s)175 static int mailstream_low_socket_close(mailstream_low * s)
176 {
177 struct mailstream_socket_data * socket_data;
178
179 socket_data = (struct mailstream_socket_data *) s->data;
180 socket_data_close(socket_data);
181
182 return 0;
183 }
184
mailstream_low_socket_free(mailstream_low * s)185 static void mailstream_low_socket_free(mailstream_low * s)
186 {
187 struct mailstream_socket_data * socket_data;
188
189 socket_data = (struct mailstream_socket_data *) s->data;
190 socket_data_free(socket_data);
191 s->data = NULL;
192
193 free(s);
194 }
195
mailstream_low_socket_get_fd(mailstream_low * s)196 static int mailstream_low_socket_get_fd(mailstream_low * s)
197 {
198 struct mailstream_socket_data * socket_data;
199
200 socket_data = (struct mailstream_socket_data *) s->data;
201 return socket_data->fd;
202 }
203
204
mailstream_low_socket_read(mailstream_low * s,void * buf,size_t count)205 static ssize_t mailstream_low_socket_read(mailstream_low * s,
206 void * buf, size_t count)
207 {
208 struct mailstream_socket_data * socket_data;
209
210 socket_data = (struct mailstream_socket_data *) s->data;
211
212 if (mailstream_cancel_cancelled(socket_data->cancel))
213 return -1;
214
215 /* timeout */
216 {
217 struct timeval timeout;
218 int r;
219 int cancellation_fd;
220 int cancelled;
221 int got_data;
222 #if defined(WIN32)
223 fd_set fds_read;
224 HANDLE event;
225 #elif USE_POLL
226 struct pollfd pfd[2];
227 #else
228 fd_set fds_read;
229 int max_fd;
230 #endif
231
232 if (s->timeout == 0) {
233 timeout = mailstream_network_delay;
234 }
235 else {
236 timeout.tv_sec = s->timeout;
237 timeout.tv_usec = 0;
238 }
239
240 cancellation_fd = mailstream_cancel_get_fd(socket_data->cancel);
241
242 #if defined(WIN32)
243 FD_ZERO(&fds_read);
244 FD_SET(cancellation_fd, &fds_read);
245
246 event = CreateEvent(NULL, TRUE, FALSE, NULL);
247 WSAEventSelect(socket_data->fd, event, FD_READ | FD_CLOSE);
248 FD_SET(event, &fds_read);
249 r = WaitForMultipleObjects(fds_read.fd_count, fds_read.fd_array, FALSE, timeout.tv_sec * 1000 + timeout.tv_usec / 1000);
250 if (WAIT_TIMEOUT == r) {
251 WSAEventSelect(socket_data->fd, event, 0);
252 CloseHandle(event);
253 return -1;
254 }
255
256 cancelled = (fds_read.fd_array[r - WAIT_OBJECT_0] == cancellation_fd);
257 got_data = (fds_read.fd_array[r - WAIT_OBJECT_0] == event);
258 WSAEventSelect(socket_data->fd, event, 0);
259 CloseHandle(event);
260 #elif USE_POLL
261 pfd[0].fd = socket_data->fd;
262 pfd[0].events = POLLIN;
263 pfd[0].revents = 0;
264
265 pfd[1].fd = cancellation_fd;
266 pfd[1].events = POLLIN;
267 pfd[1].revents = 0;
268
269 r = poll(&pfd[0], 2, timeout.tv_sec * 1000 + timeout.tv_usec / 1000);
270 if (r <= 0)
271 return -1;
272
273 cancelled = pfd[1].revents & POLLIN;
274 got_data = pfd[0].revents & POLLIN;
275 #else
276 FD_ZERO(&fds_read);
277 FD_SET(cancellation_fd, &fds_read);
278 FD_SET(socket_data->fd, &fds_read);
279 max_fd = cancellation_fd > socket_data->fd ? cancellation_fd : socket_data->fd;
280 r = select(max_fd + 1, &fds_read, NULL,/* &fds_excp*/ NULL, &timeout);
281 if (r <= 0)
282 return -1;
283
284 cancelled = FD_ISSET(cancellation_fd, &fds_read);
285 got_data = FD_ISSET(socket_data->fd, &fds_read);
286 #endif
287
288 if (cancelled) {
289 /* cancelled */
290 mailstream_cancel_ack(socket_data->cancel);
291 return -1;
292 }
293
294 if (!got_data)
295 return 0;
296 }
297
298 if (socket_data->use_read) {
299 return read(socket_data->fd, buf, count);
300 }
301 else {
302 return recv(socket_data->fd, buf, count, 0);
303 }
304 }
305
mailstream_low_socket_write(mailstream_low * s,const void * buf,size_t count)306 static ssize_t mailstream_low_socket_write(mailstream_low * s,
307 const void * buf, size_t count)
308 {
309 struct mailstream_socket_data * socket_data;
310
311 socket_data = (struct mailstream_socket_data *) s->data;
312
313 if (mailstream_cancel_cancelled(socket_data->cancel))
314 return -1;
315
316 /* timeout */
317 {
318 struct timeval timeout;
319 int r;
320 int cancellation_fd;
321 int cancelled;
322 int write_enabled;
323 #if defined(WIN32)
324 fd_set fds_read;
325 fd_set fds_write;
326 HANDLE event;
327 #elif USE_POLL
328 struct pollfd pfd[2];
329 #else
330 fd_set fds_read;
331 fd_set fds_write;
332 int max_fd;
333 #endif
334
335 if (s->timeout == 0) {
336 timeout = mailstream_network_delay;
337 }
338 else {
339 timeout.tv_sec = s->timeout;
340 timeout.tv_usec = 0;
341 }
342
343 cancellation_fd = mailstream_cancel_get_fd(socket_data->cancel);
344 #if defined(WIN32)
345 FD_ZERO(&fds_read);
346 FD_SET(cancellation_fd, &fds_read);
347 FD_ZERO(&fds_write);
348
349 event = CreateEvent(NULL, TRUE, FALSE, NULL);
350 WSAEventSelect(socket_data->fd, event, FD_WRITE | FD_CLOSE);
351 FD_SET(event, &fds_read);
352 r = WaitForMultipleObjects(fds_read.fd_count, fds_read.fd_array, FALSE, timeout.tv_sec * 1000 + timeout.tv_usec / 1000);
353 if (r < 0) {
354 WSAEventSelect(socket_data->fd, event, 0);
355 CloseHandle(event);
356 return -1;
357 }
358
359 cancelled = (fds_read.fd_array[r - WAIT_OBJECT_0] == cancellation_fd);
360 write_enabled = (fds_read.fd_array[r - WAIT_OBJECT_0] == event);
361 WSAEventSelect(socket_data->fd, event, 0);
362 CloseHandle(event);
363 #elif USE_POLL
364 pfd[0].fd = socket_data->fd;
365 pfd[0].events = POLLOUT;
366 pfd[0].revents = 0;
367
368 pfd[1].fd = cancellation_fd;
369 pfd[1].events = POLLIN;
370 pfd[1].revents = 0;
371
372 r = poll(&pfd[0], 2, timeout.tv_sec * 1000 + timeout.tv_usec / 1000);
373 if (r <= 0)
374 return -1;
375
376 cancelled = pfd[1].revents & POLLIN;
377 write_enabled = pfd[0].revents & POLLOUT;
378 #else
379 FD_ZERO(&fds_read);
380 FD_SET(cancellation_fd, &fds_read);
381 FD_ZERO(&fds_write);
382 FD_SET(socket_data->fd, &fds_write);
383 max_fd = cancellation_fd > socket_data->fd ? cancellation_fd : socket_data->fd;
384 r = select(max_fd + 1, &fds_read, &fds_write, /*&fds_excp */ NULL, &timeout);
385 if (r <= 0)
386 return -1;
387
388 cancelled = FD_ISSET(cancellation_fd, &fds_read);
389 write_enabled = FD_ISSET(socket_data->fd, &fds_write);
390 #endif
391
392 if (cancelled) {
393 /* cancelled */
394 mailstream_cancel_ack(socket_data->cancel);
395 return -1;
396 }
397
398 if (!write_enabled)
399 return 0;
400 }
401
402 return send(socket_data->fd, buf, count, 0);
403 }
404
405
406 /* mailstream */
407
mailstream_socket_open(int fd)408 mailstream * mailstream_socket_open(int fd)
409 {
410 return mailstream_socket_open_timeout(fd, 0);
411 }
412
mailstream_socket_open_timeout(int fd,time_t timeout)413 mailstream * mailstream_socket_open_timeout(int fd, time_t timeout)
414 {
415 mailstream_low * low;
416 mailstream * s;
417
418 low = mailstream_low_socket_open(fd);
419 if (low == NULL)
420 goto err;
421 mailstream_low_set_timeout(low, timeout);
422
423 s = mailstream_new(low, 8192);
424 if (s == NULL)
425 goto free_low;
426
427 return s;
428
429 free_low:
430 mailstream_low_close(low);
431 err:
432 return NULL;
433 }
434
mailstream_low_socket_cancel(mailstream_low * s)435 static void mailstream_low_socket_cancel(mailstream_low * s)
436 {
437 struct mailstream_socket_data * socket_data;
438
439 socket_data = (struct mailstream_socket_data *) s->data;
440 mailstream_cancel_notify(socket_data->cancel);
441 }
442
mailstream_socket_set_use_read(mailstream * stream,int use_read)443 void mailstream_socket_set_use_read(mailstream * stream, int use_read)
444 {
445 struct mailstream_socket_data * socket_data;
446 mailstream_low * low;
447
448 low = mailstream_get_low(stream);
449 socket_data = (struct mailstream_socket_data *) low->data;
450 socket_data->use_read = use_read;
451 }
452
mailstream_low_socket_get_cancel(mailstream_low * s)453 static struct mailstream_cancel * mailstream_low_socket_get_cancel(mailstream_low * s)
454 {
455 struct mailstream_socket_data * socket_data;
456
457 socket_data = (struct mailstream_socket_data *) s->data;
458 return socket_data->cancel;
459 }
460