diff options
Diffstat (limited to 'src/transport/plugin_transport_smtp.c')
-rw-r--r-- | src/transport/plugin_transport_smtp.c | 906 |
1 files changed, 906 insertions, 0 deletions
diff --git a/src/transport/plugin_transport_smtp.c b/src/transport/plugin_transport_smtp.c new file mode 100644 index 000000000..f7cc530e4 --- /dev/null +++ b/src/transport/plugin_transport_smtp.c | |||
@@ -0,0 +1,906 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet | ||
3 | (C) 2003, 2004, 2005, 2006, 2007 Christian Grothoff (and other contributing authors) | ||
4 | |||
5 | GNUnet is free software; you can redistribute it and/or modify | ||
6 | it under the terms of the GNU General Public License as published | ||
7 | by the Free Software Foundation; either version 2, or (at your | ||
8 | option) any later version. | ||
9 | |||
10 | GNUnet is distributed in the hope that it will be useful, but | ||
11 | WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU General Public License | ||
16 | along with GNUnet; see the file COPYING. If not, write to the | ||
17 | Free Software Foundation, Inc., 59 Temple Place - Suite 330, | ||
18 | Boston, MA 02111-1307, USA. | ||
19 | */ | ||
20 | |||
21 | /** | ||
22 | * @file transports/smtp.c | ||
23 | * @brief Implementation of the SMTP transport service | ||
24 | * @author Christian Grothoff | ||
25 | * @author Renaldo Ferreira | ||
26 | */ | ||
27 | |||
28 | #include "platform.h" | ||
29 | #include "gnunet_util.h" | ||
30 | #include "gnunet_directories.h" | ||
31 | #include "gnunet_protocols.h" | ||
32 | #include "gnunet_transport.h" | ||
33 | #include "gnunet_stats_service.h" | ||
34 | #include <libesmtp.h> | ||
35 | #include <signal.h> | ||
36 | |||
37 | |||
38 | /** | ||
39 | * The default maximum size of each outbound SMTP message. | ||
40 | */ | ||
41 | #define SMTP_MESSAGE_SIZE 65528 | ||
42 | |||
43 | #define DEBUG_SMTP GNUNET_NO | ||
44 | |||
45 | #define FILTER_STRING_SIZE 64 | ||
46 | |||
47 | /* how long can a line in base64 encoded | ||
48 | mime text be? (in characters, excluding "\n") */ | ||
49 | #define MAX_CHAR_PER_LINE 76 | ||
50 | |||
51 | #define EBUF_LEN 128 | ||
52 | |||
53 | /** | ||
54 | * Host-Address in a SMTP network. | ||
55 | */ | ||
56 | typedef struct | ||
57 | { | ||
58 | |||
59 | /** | ||
60 | * Filter line that every sender must include in the E-mails such | ||
61 | * that the receiver can effectively filter out the GNUnet traffic | ||
62 | * from the E-mail. | ||
63 | */ | ||
64 | char filter[FILTER_STRING_SIZE]; | ||
65 | |||
66 | /** | ||
67 | * Claimed E-mail address of the sender. | ||
68 | * Format is "foo@bar.com" with null termination, padded to be | ||
69 | * of a multiple of 8 bytes long. | ||
70 | */ | ||
71 | char senderAddress[0]; | ||
72 | |||
73 | } EmailAddress; | ||
74 | |||
75 | /** | ||
76 | * Encapsulation of a GNUnet message in the SMTP mail body (before | ||
77 | * base64 encoding). | ||
78 | */ | ||
79 | typedef struct | ||
80 | { | ||
81 | GNUNET_MessageHeader header; | ||
82 | |||
83 | /** | ||
84 | * What is the identity of the sender (GNUNET_hash of public key) | ||
85 | */ | ||
86 | GNUNET_PeerIdentity sender; | ||
87 | |||
88 | } SMTPMessage; | ||
89 | |||
90 | /* *********** globals ************* */ | ||
91 | |||
92 | /** | ||
93 | * apis (our advertised API and the core api ) | ||
94 | */ | ||
95 | static GNUNET_CoreAPIForTransport *coreAPI; | ||
96 | |||
97 | static struct GNUNET_GE_Context *ectx; | ||
98 | |||
99 | /** | ||
100 | * Thread that listens for inbound messages | ||
101 | */ | ||
102 | static struct GNUNET_ThreadHandle *dispatchThread; | ||
103 | |||
104 | /** | ||
105 | * Flag to indicate that server has been shut down. | ||
106 | */ | ||
107 | static int smtp_shutdown = GNUNET_YES; | ||
108 | |||
109 | /** | ||
110 | * Set to the SMTP server hostname (and port) for outgoing messages. | ||
111 | */ | ||
112 | static char *smtp_server_name; | ||
113 | |||
114 | static char *pipename; | ||
115 | |||
116 | /** | ||
117 | * Lock for uses of libesmtp (not thread-safe). | ||
118 | */ | ||
119 | static struct GNUNET_Mutex *lock; | ||
120 | |||
121 | /** | ||
122 | * Old handler for SIGPIPE (kept to be able to restore). | ||
123 | */ | ||
124 | static struct sigaction old_handler; | ||
125 | |||
126 | static char *email; | ||
127 | |||
128 | static GNUNET_TransportAPI smtpAPI; | ||
129 | |||
130 | static GNUNET_Stats_ServiceAPI *stats; | ||
131 | |||
132 | static int stat_bytesReceived; | ||
133 | |||
134 | static int stat_bytesSent; | ||
135 | |||
136 | static int stat_bytesDropped; | ||
137 | |||
138 | /** | ||
139 | * How many e-mails are we allowed to send per hour? | ||
140 | */ | ||
141 | static unsigned long long rate_limit; | ||
142 | |||
143 | static GNUNET_CronTime last_transmission; | ||
144 | |||
145 | /** ******************** Base64 encoding ***********/ | ||
146 | |||
147 | #define FILLCHAR '=' | ||
148 | static char *cvt = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" | ||
149 | "abcdefghijklmnopqrstuvwxyz" "0123456789+/"; | ||
150 | |||
151 | /** | ||
152 | * Encode into Base64. | ||
153 | * | ||
154 | * @param data the data to encode | ||
155 | * @param len the length of the input | ||
156 | * @param output where to write the output (*output should be NULL, | ||
157 | * is allocated) | ||
158 | * @return the size of the output | ||
159 | */ | ||
160 | static unsigned int | ||
161 | base64_encode (const char *data, unsigned int len, char **output) | ||
162 | { | ||
163 | unsigned int i; | ||
164 | char c; | ||
165 | unsigned int ret; | ||
166 | char *opt; | ||
167 | |||
168 | /* (*output)[ret++] = '\r'; \*/ | ||
169 | #define CHECKLINE \ | ||
170 | if ( (ret % MAX_CHAR_PER_LINE) == 0) { \ | ||
171 | (*output)[ret++] = '\n'; \ | ||
172 | } | ||
173 | ret = 0; | ||
174 | opt = GNUNET_malloc (2 + (((len * 4 / 3) + 8) * (MAX_CHAR_PER_LINE + 2)) / | ||
175 | MAX_CHAR_PER_LINE); | ||
176 | /* message must start with \r\n for libesmtp */ | ||
177 | *output = opt; | ||
178 | opt[0] = '\r'; | ||
179 | opt[1] = '\n'; | ||
180 | ret += 2; | ||
181 | for (i = 0; i < len; ++i) | ||
182 | { | ||
183 | c = (data[i] >> 2) & 0x3f; | ||
184 | opt[ret++] = cvt[(int) c]; | ||
185 | CHECKLINE; | ||
186 | c = (data[i] << 4) & 0x3f; | ||
187 | if (++i < len) | ||
188 | c |= (data[i] >> 4) & 0x0f; | ||
189 | opt[ret++] = cvt[(int) c]; | ||
190 | CHECKLINE; | ||
191 | if (i < len) | ||
192 | { | ||
193 | c = (data[i] << 2) & 0x3f; | ||
194 | if (++i < len) | ||
195 | c |= (data[i] >> 6) & 0x03; | ||
196 | opt[ret++] = cvt[(int) c]; | ||
197 | CHECKLINE; | ||
198 | } | ||
199 | else | ||
200 | { | ||
201 | ++i; | ||
202 | opt[ret++] = FILLCHAR; | ||
203 | CHECKLINE; | ||
204 | } | ||
205 | if (i < len) | ||
206 | { | ||
207 | c = data[i] & 0x3f; | ||
208 | opt[ret++] = cvt[(int) c]; | ||
209 | CHECKLINE; | ||
210 | } | ||
211 | else | ||
212 | { | ||
213 | opt[ret++] = FILLCHAR; | ||
214 | CHECKLINE; | ||
215 | } | ||
216 | } | ||
217 | opt[ret++] = FILLCHAR; | ||
218 | return ret; | ||
219 | } | ||
220 | |||
221 | #define cvtfind(a)( (((a) >= 'A')&&((a) <= 'Z'))? (a)-'A'\ | ||
222 | :(((a)>='a')&&((a)<='z')) ? (a)-'a'+26\ | ||
223 | :(((a)>='0')&&((a)<='9')) ? (a)-'0'+52\ | ||
224 | :((a) == '+') ? 62\ | ||
225 | :((a) == '/') ? 63 : -1) | ||
226 | /** | ||
227 | * Decode from Base64. | ||
228 | * | ||
229 | * @param data the data to encode | ||
230 | * @param len the length of the input | ||
231 | * @param output where to write the output (*output should be NULL, | ||
232 | * is allocated) | ||
233 | * @return the size of the output | ||
234 | */ | ||
235 | static unsigned int | ||
236 | base64_decode (const char *data, unsigned int len, char **output) | ||
237 | { | ||
238 | unsigned int i; | ||
239 | char c; | ||
240 | char c1; | ||
241 | unsigned int ret = 0; | ||
242 | |||
243 | #define CHECK_CRLF while (data[i] == '\r' || data[i] == '\n') {\ | ||
244 | GNUNET_GE_LOG(ectx, GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER, "ignoring CR/LF\n"); \ | ||
245 | i++; \ | ||
246 | if (i >= len) goto END; \ | ||
247 | } | ||
248 | |||
249 | *output = GNUNET_malloc ((len * 3 / 4) + 8); | ||
250 | #if DEBUG_SMTP | ||
251 | GNUNET_GE_LOG (ectx, GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER, | ||
252 | "base64_decode decoding len=%d\n", len); | ||
253 | #endif | ||
254 | for (i = 0; i < len; ++i) | ||
255 | { | ||
256 | CHECK_CRLF; | ||
257 | if (data[i] == FILLCHAR) | ||
258 | break; | ||
259 | c = (char) cvtfind (data[i]); | ||
260 | ++i; | ||
261 | CHECK_CRLF; | ||
262 | c1 = (char) cvtfind (data[i]); | ||
263 | c = (c << 2) | ((c1 >> 4) & 0x3); | ||
264 | (*output)[ret++] = c; | ||
265 | if (++i < len) | ||
266 | { | ||
267 | CHECK_CRLF; | ||
268 | c = data[i]; | ||
269 | if (FILLCHAR == c) | ||
270 | break; | ||
271 | c = (char) cvtfind (c); | ||
272 | c1 = ((c1 << 4) & 0xf0) | ((c >> 2) & 0xf); | ||
273 | (*output)[ret++] = c1; | ||
274 | } | ||
275 | if (++i < len) | ||
276 | { | ||
277 | CHECK_CRLF; | ||
278 | c1 = data[i]; | ||
279 | if (FILLCHAR == c1) | ||
280 | break; | ||
281 | |||
282 | c1 = (char) cvtfind (c1); | ||
283 | c = ((c << 6) & 0xc0) | c1; | ||
284 | (*output)[ret++] = c; | ||
285 | } | ||
286 | } | ||
287 | END: | ||
288 | return ret; | ||
289 | } | ||
290 | |||
291 | /* ********************* the real stuff ******************* */ | ||
292 | |||
293 | #define strAUTOncmp(a,b) strncmp(a,b,strlen(b)) | ||
294 | |||
295 | /** | ||
296 | * Listen to the pipe, decode messages and send to core. | ||
297 | */ | ||
298 | static void * | ||
299 | listenAndDistribute (void *unused) | ||
300 | { | ||
301 | char *line; | ||
302 | unsigned int linesize; | ||
303 | SMTPMessage *mp; | ||
304 | FILE *fdes; | ||
305 | char *retl; | ||
306 | char *out; | ||
307 | unsigned int size; | ||
308 | GNUNET_TransportPacket *coreMP; | ||
309 | int fd; | ||
310 | unsigned int pos; | ||
311 | |||
312 | linesize = ((GNUNET_MAX_BUFFER_SIZE * 4 / 3) + 8) * (MAX_CHAR_PER_LINE + 2) / MAX_CHAR_PER_LINE; /* maximum size of a line supported */ | ||
313 | line = GNUNET_malloc (linesize + 2); /* 2 bytes for off-by-one errors, just to be safe... */ | ||
314 | |||
315 | #define READLINE(l,limit) \ | ||
316 | do { retl = fgets(l, (limit), fdes); \ | ||
317 | if ( (retl == NULL) || (smtp_shutdown == GNUNET_YES)) {\ | ||
318 | goto END; \ | ||
319 | }\ | ||
320 | if (coreAPI->load_monitor != NULL) \ | ||
321 | GNUNET_network_monitor_notify_transmission(coreAPI->load_monitor, GNUNET_ND_DOWNLOAD, strlen(retl)); \ | ||
322 | } while (0) | ||
323 | |||
324 | |||
325 | while (smtp_shutdown == GNUNET_NO) | ||
326 | { | ||
327 | fd = OPEN (pipename, O_RDONLY | O_ASYNC); | ||
328 | if (fd == -1) | ||
329 | { | ||
330 | if (smtp_shutdown == GNUNET_NO) | ||
331 | GNUNET_thread_sleep (5 * GNUNET_CRON_SECONDS); | ||
332 | continue; | ||
333 | } | ||
334 | fdes = fdopen (fd, "r"); | ||
335 | while (smtp_shutdown == GNUNET_NO) | ||
336 | { | ||
337 | /* skip until end of header */ | ||
338 | do | ||
339 | { | ||
340 | READLINE (line, linesize); | ||
341 | } | ||
342 | while ((line[0] != '\r') && (line[0] != '\n')); /* expect newline */ | ||
343 | READLINE (line, linesize); /* read base64 encoded message; decode, process */ | ||
344 | pos = 0; | ||
345 | while (1) | ||
346 | { | ||
347 | pos = strlen (line) - 1; /* ignore new line */ | ||
348 | READLINE (&line[pos], linesize - pos); /* read base64 encoded message; decode, process */ | ||
349 | if ((line[pos] == '\r') || (line[pos] == '\n')) | ||
350 | break; /* empty line => end of message! */ | ||
351 | } | ||
352 | size = base64_decode (line, pos, &out); | ||
353 | if (size < sizeof (SMTPMessage)) | ||
354 | { | ||
355 | GNUNET_GE_BREAK (ectx, 0); | ||
356 | GNUNET_free (out); | ||
357 | goto END; | ||
358 | } | ||
359 | |||
360 | mp = (SMTPMessage *) & out[size - sizeof (SMTPMessage)]; | ||
361 | if (ntohs (mp->header.size) != size) | ||
362 | { | ||
363 | GNUNET_GE_LOG (ectx, | ||
364 | GNUNET_GE_WARNING | GNUNET_GE_BULK | | ||
365 | GNUNET_GE_USER, | ||
366 | _ | ||
367 | ("Received malformed message via %s. Ignored.\n"), | ||
368 | "SMTP"); | ||
369 | #if DEBUG_SMTP | ||
370 | GNUNET_GE_LOG (ectx, | ||
371 | GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | | ||
372 | GNUNET_GE_USER, | ||
373 | "Size returned by base64=%d, in the msg=%d.\n", | ||
374 | size, ntohl (mp->size)); | ||
375 | #endif | ||
376 | GNUNET_free (out); | ||
377 | goto END; | ||
378 | } | ||
379 | if (stats != NULL) | ||
380 | stats->change (stat_bytesReceived, size); | ||
381 | coreMP = GNUNET_malloc (sizeof (GNUNET_TransportPacket)); | ||
382 | coreMP->msg = out; | ||
383 | coreMP->size = size - sizeof (SMTPMessage); | ||
384 | coreMP->tsession = NULL; | ||
385 | coreMP->sender = mp->sender; | ||
386 | #if DEBUG_SMTP | ||
387 | GNUNET_GE_LOG (ectx, | ||
388 | GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER, | ||
389 | "SMTP message passed to the core.\n"); | ||
390 | #endif | ||
391 | |||
392 | coreAPI->receive (coreMP); | ||
393 | } | ||
394 | END: | ||
395 | #if DEBUG_SMTP | ||
396 | GNUNET_GE_LOG (ectx, | ||
397 | GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER, | ||
398 | "SMTP message processed.\n"); | ||
399 | #endif | ||
400 | if (fdes != NULL) | ||
401 | fclose (fdes); | ||
402 | } | ||
403 | GNUNET_free (line); | ||
404 | return NULL; | ||
405 | } | ||
406 | |||
407 | /* *************** API implementation *************** */ | ||
408 | |||
409 | /** | ||
410 | * Verify that a hello-Message is correct (a node is reachable at that | ||
411 | * address). Since the reply will be asynchronous, a method must be | ||
412 | * called on success. | ||
413 | * | ||
414 | * @param hello the hello message to verify | ||
415 | * (the signature/crc have been verified before) | ||
416 | * @return GNUNET_OK on success, GNUNET_SYSERR on error | ||
417 | */ | ||
418 | static int | ||
419 | api_verify_hello (const GNUNET_MessageHello * hello) | ||
420 | { | ||
421 | const EmailAddress *maddr; | ||
422 | |||
423 | maddr = (const EmailAddress *) &hello[1]; | ||
424 | if ((ntohs (hello->header.size) != | ||
425 | sizeof (GNUNET_MessageHello) + ntohs (hello->senderAddressSize)) || | ||
426 | (maddr->senderAddress[ntohs (hello->senderAddressSize) - 1 - | ||
427 | FILTER_STRING_SIZE] != '\0')) | ||
428 | { | ||
429 | GNUNET_GE_BREAK (ectx, 0); | ||
430 | return GNUNET_SYSERR; /* obviously invalid */ | ||
431 | } | ||
432 | if (NULL == strstr (maddr->filter, ": ")) | ||
433 | return GNUNET_SYSERR; | ||
434 | return GNUNET_OK; | ||
435 | } | ||
436 | |||
437 | /** | ||
438 | * Create a hello-Message for the current node. The hello is created | ||
439 | * without signature and without a timestamp. The GNUnet core will | ||
440 | * GNUNET_RSA_sign the message and add an expiration time. | ||
441 | * | ||
442 | * @return hello on success, NULL on error | ||
443 | */ | ||
444 | static GNUNET_MessageHello * | ||
445 | api_create_hello () | ||
446 | { | ||
447 | GNUNET_MessageHello *msg; | ||
448 | char *filter; | ||
449 | EmailAddress *haddr; | ||
450 | int i; | ||
451 | |||
452 | GNUNET_GC_get_configuration_value_string (coreAPI->cfg, | ||
453 | "SMTP", "FILTER", | ||
454 | "X-mailer: GNUnet", &filter); | ||
455 | if (NULL == strstr (filter, ": ")) | ||
456 | { | ||
457 | GNUNET_GE_LOG (ectx, | ||
458 | GNUNET_GE_WARNING | GNUNET_GE_BULK | GNUNET_GE_USER, | ||
459 | _("SMTP filter string to invalid, lacks ': '\n")); | ||
460 | GNUNET_free (filter); | ||
461 | return NULL; | ||
462 | } | ||
463 | |||
464 | if (strlen (filter) > FILTER_STRING_SIZE) | ||
465 | { | ||
466 | filter[FILTER_STRING_SIZE] = '\0'; | ||
467 | GNUNET_GE_LOG (ectx, | ||
468 | GNUNET_GE_WARNING | GNUNET_GE_BULK | GNUNET_GE_USER, | ||
469 | _("SMTP filter string to long, capped to `%s'\n"), | ||
470 | filter); | ||
471 | } | ||
472 | i = (strlen (email) + 8) & (~7); /* make multiple of 8 */ | ||
473 | msg = | ||
474 | GNUNET_malloc (sizeof (GNUNET_MessageHello) + sizeof (EmailAddress) + i); | ||
475 | memset (msg, 0, sizeof (GNUNET_MessageHello) + sizeof (EmailAddress) + i); | ||
476 | haddr = (EmailAddress *) & msg[1]; | ||
477 | memset (&haddr->filter[0], 0, FILTER_STRING_SIZE); | ||
478 | strcpy (&haddr->filter[0], filter); | ||
479 | memcpy (&haddr->senderAddress[0], email, strlen (email) + 1); | ||
480 | msg->senderAddressSize = htons (strlen (email) + 1 + sizeof (EmailAddress)); | ||
481 | msg->protocol = htons (GNUNET_TRANSPORT_PROTOCOL_NUMBER_SMTP); | ||
482 | msg->MTU = htonl (smtpAPI.mtu); | ||
483 | msg->header.size = htons (GNUNET_sizeof_hello (msg)); | ||
484 | if (api_verify_hello (msg) == GNUNET_SYSERR) | ||
485 | GNUNET_GE_ASSERT (ectx, 0); | ||
486 | GNUNET_free (filter); | ||
487 | return msg; | ||
488 | } | ||
489 | |||
490 | struct GetMessageClosure | ||
491 | { | ||
492 | unsigned int esize; | ||
493 | unsigned int pos; | ||
494 | char *ebody; | ||
495 | }; | ||
496 | |||
497 | static const char * | ||
498 | get_message (void **buf, int *len, void *cls) | ||
499 | { | ||
500 | struct GetMessageClosure *gmc = cls; | ||
501 | |||
502 | *buf = NULL; | ||
503 | if (len == NULL) | ||
504 | { | ||
505 | gmc->pos = 0; | ||
506 | return NULL; | ||
507 | } | ||
508 | if (gmc->pos == gmc->esize) | ||
509 | return NULL; /* done */ | ||
510 | *len = gmc->esize; | ||
511 | gmc->pos = gmc->esize; | ||
512 | return gmc->ebody; | ||
513 | } | ||
514 | |||
515 | /** | ||
516 | * Send a message to the specified remote node. | ||
517 | * | ||
518 | * @param tsession the GNUNET_MessageHello identifying the remote node | ||
519 | * @param message what to send | ||
520 | * @param size the size of the message | ||
521 | * @return GNUNET_SYSERR on error, GNUNET_OK on success | ||
522 | */ | ||
523 | static int | ||
524 | api_send (GNUNET_TSession * tsession, | ||
525 | const void *msg, const unsigned int size, int important) | ||
526 | { | ||
527 | const GNUNET_MessageHello *hello; | ||
528 | const EmailAddress *haddr; | ||
529 | char *m; | ||
530 | char *filter; | ||
531 | char *fvalue; | ||
532 | SMTPMessage *mp; | ||
533 | struct GetMessageClosure gm_cls; | ||
534 | smtp_session_t session; | ||
535 | smtp_message_t message; | ||
536 | smtp_recipient_t recipient; | ||
537 | #define EBUF_LEN 128 | ||
538 | char ebuf[EBUF_LEN]; | ||
539 | GNUNET_CronTime now; | ||
540 | |||
541 | if (smtp_shutdown == GNUNET_YES) | ||
542 | return GNUNET_SYSERR; | ||
543 | if ((size == 0) || (size > smtpAPI.mtu)) | ||
544 | { | ||
545 | GNUNET_GE_BREAK (ectx, 0); | ||
546 | return GNUNET_SYSERR; | ||
547 | } | ||
548 | now = GNUNET_get_time (); | ||
549 | if ((important != GNUNET_YES) && | ||
550 | ((now - last_transmission) * rate_limit) < GNUNET_CRON_HOURS) | ||
551 | return GNUNET_NO; /* rate too high */ | ||
552 | last_transmission = now; | ||
553 | |||
554 | hello = (const GNUNET_MessageHello *) tsession->internal; | ||
555 | if (hello == NULL) | ||
556 | return GNUNET_SYSERR; | ||
557 | GNUNET_mutex_lock (lock); | ||
558 | session = smtp_create_session (); | ||
559 | if (session == NULL) | ||
560 | { | ||
561 | GNUNET_GE_LOG (ectx, | ||
562 | GNUNET_GE_ERROR | GNUNET_GE_ADMIN | GNUNET_GE_USER | | ||
563 | GNUNET_GE_IMMEDIATE, | ||
564 | _("SMTP: `%s' failed: %s.\n"), | ||
565 | "smtp_create_session", | ||
566 | smtp_strerror (smtp_errno (), ebuf, EBUF_LEN)); | ||
567 | GNUNET_mutex_unlock (lock); | ||
568 | return GNUNET_SYSERR; | ||
569 | } | ||
570 | if (0 == smtp_set_server (session, smtp_server_name)) | ||
571 | { | ||
572 | GNUNET_GE_LOG (ectx, | ||
573 | GNUNET_GE_ERROR | GNUNET_GE_ADMIN | GNUNET_GE_USER | | ||
574 | GNUNET_GE_IMMEDIATE, | ||
575 | _("SMTP: `%s' failed: %s.\n"), | ||
576 | "smtp_set_server", | ||
577 | smtp_strerror (smtp_errno (), ebuf, EBUF_LEN)); | ||
578 | smtp_destroy_session (session); | ||
579 | GNUNET_mutex_unlock (lock); | ||
580 | return GNUNET_SYSERR; | ||
581 | } | ||
582 | haddr = (const EmailAddress *) &hello[1]; | ||
583 | message = smtp_add_message (session); | ||
584 | if (message == NULL) | ||
585 | { | ||
586 | GNUNET_GE_LOG (ectx, | ||
587 | GNUNET_GE_WARNING | GNUNET_GE_ADMIN | GNUNET_GE_USER | | ||
588 | GNUNET_GE_BULK, | ||
589 | _("SMTP: `%s' failed: %s.\n"), | ||
590 | "smtp_add_message", | ||
591 | smtp_strerror (smtp_errno (), ebuf, EBUF_LEN)); | ||
592 | smtp_destroy_session (session); | ||
593 | GNUNET_mutex_unlock (lock); | ||
594 | return GNUNET_SYSERR; | ||
595 | } | ||
596 | smtp_set_header (message, "To", NULL, haddr->senderAddress); | ||
597 | smtp_set_header (message, "From", NULL, email); | ||
598 | |||
599 | filter = GNUNET_strdup (haddr->filter); | ||
600 | fvalue = strstr (filter, ": "); | ||
601 | GNUNET_GE_ASSERT (NULL, NULL != fvalue); | ||
602 | fvalue[0] = '\0'; | ||
603 | fvalue += 2; | ||
604 | if (0 == smtp_set_header (message, filter, fvalue)) | ||
605 | { | ||
606 | GNUNET_GE_LOG (ectx, | ||
607 | GNUNET_GE_WARNING | GNUNET_GE_ADMIN | GNUNET_GE_USER | | ||
608 | GNUNET_GE_BULK, | ||
609 | _("SMTP: `%s' failed: %s.\n"), | ||
610 | "smtp_set_header", | ||
611 | smtp_strerror (smtp_errno (), ebuf, EBUF_LEN)); | ||
612 | smtp_destroy_session (session); | ||
613 | GNUNET_mutex_unlock (lock); | ||
614 | GNUNET_free (filter); | ||
615 | return GNUNET_SYSERR; | ||
616 | } | ||
617 | GNUNET_free (filter); | ||
618 | m = GNUNET_malloc (size + sizeof (SMTPMessage)); | ||
619 | memcpy (m, msg, size); | ||
620 | mp = (SMTPMessage *) & m[size]; | ||
621 | mp->header.size = htons (size + sizeof (SMTPMessage)); | ||
622 | mp->header.type = htons (0); | ||
623 | mp->sender = *coreAPI->my_identity; | ||
624 | gm_cls.ebody = NULL; | ||
625 | gm_cls.pos = 0; | ||
626 | gm_cls.esize = | ||
627 | base64_encode (m, size + sizeof (SMTPMessage), &gm_cls.ebody); | ||
628 | GNUNET_free (m); | ||
629 | if (0 == smtp_size_set_estimate (message, gm_cls.esize)) | ||
630 | { | ||
631 | GNUNET_GE_LOG (ectx, | ||
632 | GNUNET_GE_WARNING | GNUNET_GE_ADMIN | GNUNET_GE_USER | | ||
633 | GNUNET_GE_BULK, | ||
634 | _("SMTP: `%s' failed: %s.\n"), | ||
635 | "smtp_size_set_estimate", | ||
636 | smtp_strerror (smtp_errno (), ebuf, EBUF_LEN)); | ||
637 | } | ||
638 | if (0 == smtp_set_messagecb (message, &get_message, &gm_cls)) | ||
639 | { | ||
640 | GNUNET_GE_LOG (ectx, | ||
641 | GNUNET_GE_WARNING | GNUNET_GE_ADMIN | GNUNET_GE_USER | | ||
642 | GNUNET_GE_BULK, | ||
643 | _("SMTP: `%s' failed: %s.\n"), | ||
644 | "smtp_set_messagecb", | ||
645 | smtp_strerror (smtp_errno (), ebuf, EBUF_LEN)); | ||
646 | smtp_destroy_session (session); | ||
647 | GNUNET_mutex_unlock (lock); | ||
648 | GNUNET_free (gm_cls.ebody); | ||
649 | return GNUNET_SYSERR; | ||
650 | } | ||
651 | recipient = smtp_add_recipient (message, haddr->senderAddress); | ||
652 | if (recipient == NULL) | ||
653 | { | ||
654 | GNUNET_GE_LOG (ectx, | ||
655 | GNUNET_GE_WARNING | GNUNET_GE_ADMIN | GNUNET_GE_USER | | ||
656 | GNUNET_GE_BULK, | ||
657 | _("SMTP: `%s' failed: %s.\n"), | ||
658 | "smtp_add_recipient", | ||
659 | smtp_strerror (smtp_errno (), ebuf, EBUF_LEN)); | ||
660 | smtp_destroy_session (session); | ||
661 | GNUNET_mutex_unlock (lock); | ||
662 | return GNUNET_SYSERR; | ||
663 | } | ||
664 | if (0 == smtp_start_session (session)) | ||
665 | { | ||
666 | GNUNET_GE_LOG (ectx, | ||
667 | GNUNET_GE_WARNING | GNUNET_GE_ADMIN | GNUNET_GE_USER | | ||
668 | GNUNET_GE_BULK, | ||
669 | _("SMTP: `%s' failed: %s.\n"), | ||
670 | "smtp_start_session", | ||
671 | smtp_strerror (smtp_errno (), ebuf, EBUF_LEN)); | ||
672 | smtp_destroy_session (session); | ||
673 | GNUNET_mutex_unlock (lock); | ||
674 | GNUNET_free (gm_cls.ebody); | ||
675 | return GNUNET_SYSERR; | ||
676 | } | ||
677 | if (stats != NULL) | ||
678 | stats->change (stat_bytesSent, size); | ||
679 | if (coreAPI->load_monitor != NULL) | ||
680 | GNUNET_network_monitor_notify_transmission (coreAPI->load_monitor, | ||
681 | GNUNET_ND_UPLOAD, | ||
682 | gm_cls.esize); | ||
683 | smtp_message_reset_status (message); /* this is needed to plug a 28-byte/message memory leak in libesmtp */ | ||
684 | smtp_destroy_session (session); | ||
685 | GNUNET_mutex_unlock (lock); | ||
686 | GNUNET_free (gm_cls.ebody); | ||
687 | return GNUNET_OK; | ||
688 | } | ||
689 | |||
690 | /** | ||
691 | * Establish a connection to a remote node. | ||
692 | * @param helo the hello-Message for the target node | ||
693 | * @param tsessionPtr the session handle that is to be set | ||
694 | * @return GNUNET_OK on success, GNUNET_SYSERR if the operation failed | ||
695 | */ | ||
696 | static int | ||
697 | api_connect (const GNUNET_MessageHello * hello, | ||
698 | GNUNET_TSession ** tsessionPtr, int may_reuse) | ||
699 | { | ||
700 | GNUNET_TSession *tsession; | ||
701 | |||
702 | tsession = GNUNET_malloc (sizeof (GNUNET_TSession)); | ||
703 | tsession->internal = GNUNET_malloc (GNUNET_sizeof_hello (hello)); | ||
704 | tsession->peer = hello->senderIdentity; | ||
705 | memcpy (tsession->internal, hello, GNUNET_sizeof_hello (hello)); | ||
706 | tsession->ttype = smtpAPI.protocol_number; | ||
707 | (*tsessionPtr) = tsession; | ||
708 | return GNUNET_OK; | ||
709 | } | ||
710 | |||
711 | /** | ||
712 | * Disconnect from a remote node. | ||
713 | * | ||
714 | * @param tsession the session that is closed | ||
715 | * @return GNUNET_OK on success, GNUNET_SYSERR if the operation failed | ||
716 | */ | ||
717 | static int | ||
718 | api_disconnect (GNUNET_TSession * tsession) | ||
719 | { | ||
720 | if (tsession != NULL) | ||
721 | { | ||
722 | if (tsession->internal != NULL) | ||
723 | GNUNET_free (tsession->internal); | ||
724 | GNUNET_free (tsession); | ||
725 | } | ||
726 | return GNUNET_OK; | ||
727 | } | ||
728 | |||
729 | /** | ||
730 | * Start the server process to receive inbound traffic. | ||
731 | * @return GNUNET_OK on success, GNUNET_SYSERR if the operation failed | ||
732 | */ | ||
733 | static int | ||
734 | api_start_transport_server () | ||
735 | { | ||
736 | smtp_shutdown = GNUNET_NO; | ||
737 | /* initialize SMTP network */ | ||
738 | dispatchThread = | ||
739 | GNUNET_thread_create (&listenAndDistribute, NULL, 1024 * 4); | ||
740 | if (dispatchThread == NULL) | ||
741 | { | ||
742 | GNUNET_GE_DIE_STRERROR (ectx, | ||
743 | GNUNET_GE_ADMIN | GNUNET_GE_BULK | | ||
744 | GNUNET_GE_FATAL, "pthread_create"); | ||
745 | return GNUNET_SYSERR; | ||
746 | } | ||
747 | return GNUNET_OK; | ||
748 | } | ||
749 | |||
750 | /** | ||
751 | * Shutdown the server process (stop receiving inbound traffic). Maybe | ||
752 | * restarted later! | ||
753 | */ | ||
754 | static int | ||
755 | api_stop_transport_server () | ||
756 | { | ||
757 | void *unused; | ||
758 | |||
759 | smtp_shutdown = GNUNET_YES; | ||
760 | GNUNET_thread_stop_sleep (dispatchThread); | ||
761 | GNUNET_thread_join (dispatchThread, &unused); | ||
762 | return GNUNET_OK; | ||
763 | } | ||
764 | |||
765 | /** | ||
766 | * Convert SMTP hello to an IP address (always fails). | ||
767 | */ | ||
768 | static int | ||
769 | api_hello_to_address (const GNUNET_MessageHello * hello, | ||
770 | void **sa, unsigned int *sa_len) | ||
771 | { | ||
772 | return GNUNET_SYSERR; | ||
773 | } | ||
774 | |||
775 | /** | ||
776 | * Always fails. | ||
777 | */ | ||
778 | static int | ||
779 | api_associate (GNUNET_TSession * tsession) | ||
780 | { | ||
781 | return GNUNET_SYSERR; /* SMTP connections can never be associated */ | ||
782 | } | ||
783 | |||
784 | /** | ||
785 | * Always succeeds (for now; we should look at adding | ||
786 | * frequency limits to SMTP in the future!). | ||
787 | */ | ||
788 | static int | ||
789 | api_test_would_try (GNUNET_TSession * tsession, const unsigned int size, | ||
790 | int important) | ||
791 | { | ||
792 | return GNUNET_OK; /* we always try... */ | ||
793 | } | ||
794 | |||
795 | /** | ||
796 | * The exported method. Makes the core api available via a global and | ||
797 | * returns the smtp transport API. | ||
798 | */ | ||
799 | GNUNET_TransportAPI * | ||
800 | inittransport_smtp (GNUNET_CoreAPIForTransport * core) | ||
801 | { | ||
802 | |||
803 | |||
804 | unsigned long long mtu; | ||
805 | struct sigaction sa; | ||
806 | |||
807 | coreAPI = core; | ||
808 | ectx = core->ectx; | ||
809 | if (!GNUNET_GC_have_configuration_value (coreAPI->cfg, "SMTP", "EMAIL")) | ||
810 | { | ||
811 | GNUNET_GE_LOG (ectx, | ||
812 | GNUNET_GE_ERROR | GNUNET_GE_BULK | GNUNET_GE_USER, | ||
813 | _ | ||
814 | ("No email-address specified, can not start SMTP transport.\n")); | ||
815 | return NULL; | ||
816 | } | ||
817 | GNUNET_GC_get_configuration_value_number (coreAPI->cfg, | ||
818 | "SMTP", | ||
819 | "MTU", | ||
820 | 1200, | ||
821 | SMTP_MESSAGE_SIZE, | ||
822 | SMTP_MESSAGE_SIZE, &mtu); | ||
823 | GNUNET_GC_get_configuration_value_number (coreAPI->cfg, | ||
824 | "SMTP", | ||
825 | "RATELIMIT", | ||
826 | 0, 0, 1024 * 1024, &rate_limit); | ||
827 | stats = coreAPI->service_request ("stats"); | ||
828 | if (stats != NULL) | ||
829 | { | ||
830 | stat_bytesReceived | ||
831 | = stats->create (gettext_noop ("# bytes received via SMTP")); | ||
832 | stat_bytesSent = stats->create (gettext_noop ("# bytes sent via SMTP")); | ||
833 | stat_bytesDropped | ||
834 | = stats->create (gettext_noop ("# bytes dropped by SMTP (outgoing)")); | ||
835 | } | ||
836 | GNUNET_GC_get_configuration_value_filename (coreAPI->cfg, | ||
837 | "SMTP", | ||
838 | "PIPE", | ||
839 | GNUNET_DEFAULT_DAEMON_VAR_DIRECTORY | ||
840 | "/smtp-pipe", &pipename); | ||
841 | UNLINK (pipename); | ||
842 | if (0 != mkfifo (pipename, S_IWUSR | S_IRUSR | S_IWGRP | S_IWOTH)) | ||
843 | { | ||
844 | GNUNET_GE_LOG_STRERROR (ectx, | ||
845 | GNUNET_GE_ADMIN | GNUNET_GE_BULK | | ||
846 | GNUNET_GE_FATAL, "mkfifo"); | ||
847 | GNUNET_free (pipename); | ||
848 | coreAPI->service_release (stats); | ||
849 | stats = NULL; | ||
850 | return NULL; | ||
851 | } | ||
852 | /* we need to allow the mailer program to send us messages; | ||
853 | easiest done by giving it write permissions (see Mantis #1142) */ | ||
854 | if (0 != chmod (pipename, S_IWUSR | S_IRUSR | S_IWGRP | S_IWOTH)) | ||
855 | GNUNET_GE_LOG_STRERROR (ectx, | ||
856 | GNUNET_GE_ADMIN | GNUNET_GE_BULK | | ||
857 | GNUNET_GE_WARNING, "chmod"); | ||
858 | GNUNET_GC_get_configuration_value_string (coreAPI->cfg, | ||
859 | "SMTP", "EMAIL", NULL, &email); | ||
860 | lock = GNUNET_mutex_create (GNUNET_NO); | ||
861 | GNUNET_GC_get_configuration_value_string (coreAPI->cfg, | ||
862 | "SMTP", | ||
863 | "SERVER", | ||
864 | "localhost:25", | ||
865 | &smtp_server_name); | ||
866 | sa.sa_handler = SIG_IGN; | ||
867 | sigemptyset (&sa.sa_mask); | ||
868 | sa.sa_flags = 0; | ||
869 | sigaction (SIGPIPE, &sa, &old_handler); | ||
870 | |||
871 | smtpAPI.protocol_number = GNUNET_TRANSPORT_PROTOCOL_NUMBER_SMTP; | ||
872 | smtpAPI.mtu = mtu - sizeof (SMTPMessage); | ||
873 | smtpAPI.cost = 50; | ||
874 | smtpAPI.hello_verify = &api_verify_hello; | ||
875 | smtpAPI.hello_create = &api_create_hello; | ||
876 | smtpAPI.connect = &api_connect; | ||
877 | smtpAPI.send = &api_send; | ||
878 | smtpAPI.associate = &api_associate; | ||
879 | smtpAPI.disconnect = &api_disconnect; | ||
880 | smtpAPI.server_start = &api_start_transport_server; | ||
881 | smtpAPI.server_stop = &api_stop_transport_server; | ||
882 | smtpAPI.hello_to_address = &api_hello_to_address; | ||
883 | smtpAPI.send_now_test = &api_test_would_try; | ||
884 | return &smtpAPI; | ||
885 | } | ||
886 | |||
887 | void | ||
888 | donetransport_smtp () | ||
889 | { | ||
890 | sigaction (SIGPIPE, &old_handler, NULL); | ||
891 | GNUNET_free (smtp_server_name); | ||
892 | if (stats != NULL) | ||
893 | { | ||
894 | coreAPI->service_release (stats); | ||
895 | stats = NULL; | ||
896 | } | ||
897 | GNUNET_mutex_destroy (lock); | ||
898 | lock = NULL; | ||
899 | UNLINK (pipename); | ||
900 | GNUNET_free (pipename); | ||
901 | pipename = NULL; | ||
902 | GNUNET_free (email); | ||
903 | email = NULL; | ||
904 | } | ||
905 | |||
906 | /* end of smtp.c */ | ||