2 This file is part of GNUnet
3 Copyright (C) 2003-2013 GNUnet e.V.
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
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 Affero General Public License for more details.
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
18 SPDX-License-Identifier: AGPL3.0-or-later
22 * @file transport/plugin_transport_smtp.c
23 * @brief Implementation of the SMTP transport service
24 * @author Christian Grothoff
25 * @author Renaldo Ferreira
29 #include "gnunet_util.h"
30 #include "gnunet_constants.h"
31 #include "gnunet_protocols.h"
32 #include "gnunet_transport.h"
33 #include "gnunet_stats_service.h"
39 * The default maximum size of each outbound SMTP message.
41 #define SMTP_MESSAGE_SIZE 65528
43 #define DEBUG_SMTP GNUNET_EXTRA_LOGGING
45 #define FILTER_STRING_SIZE 64
47 /* how long can a line in base64 encoded
48 mime text be? (in characters, excluding "\n") */
49 #define MAX_CHAR_PER_LINE 76
54 * Host-Address in a SMTP network.
58 * Filter line that every sender must include in the E-mails such
59 * that the receiver can effectively filter out the GNUnet traffic
62 char filter[FILTER_STRING_SIZE];
65 * Claimed E-mail address of the sender.
66 * Format is "foo@bar.com" with null termination, padded to be
67 * of a multiple of 8 bytes long.
69 char senderAddress[0];
72 GNUNET_NETWORK_STRUCT_BEGIN
75 * Encapsulation of a GNUnet message in the SMTP mail body (before
79 GNUNET_MessageHeader header;
82 * What is the identity of the sender (GNUNET_hash of public key)
84 GNUNET_PeerIdentity sender;
86 GNUNET_NETWORK_STRUCT_END
88 /* *********** globals ************* */
91 * apis (our advertised API and the core api )
93 static GNUNET_CoreAPIForTransport *core_api;
95 static struct GNUNET_GE_Context *ectx;
98 * Thread that listens for inbound messages
100 static struct GNUNET_ThreadHandle *dispatchThread;
103 * Flag to indicate that server has been shut down.
105 static int smtp_shutdown = GNUNET_YES;
108 * Set to the SMTP server hostname (and port) for outgoing messages.
110 static char *smtp_server_name;
112 static char *pipename;
115 * Lock for uses of libesmtp (not thread-safe).
117 static struct GNUNET_Mutex *lock;
120 * Old handler for SIGPIPE (kept to be able to restore).
122 static struct sigaction old_handler;
126 static GNUNET_TransportAPI smtpAPI;
128 static GNUNET_Stats_ServiceAPI *stats;
130 static int stat_bytesReceived;
132 static int stat_bytesSent;
134 static int stat_bytesDropped;
137 * How many e-mails are we allowed to send per hour?
139 static unsigned long long rate_limit;
141 static GNUNET_CronTime last_transmission;
144 /* ********************* the real stuff ******************* */
146 #define strAUTOncmp(a, b) strncmp(a, b, strlen(b))
149 * Listen to the pipe, decode messages and send to core.
152 listenAndDistribute(void *unused)
155 unsigned int linesize;
161 GNUNET_TransportPacket *coreMP;
165 linesize = ((GNUNET_MAX_BUFFER_SIZE * 4 / 3) + 8) * (MAX_CHAR_PER_LINE + 2) / MAX_CHAR_PER_LINE; /* maximum size of a line supported */
166 line = GNUNET_malloc(linesize + 2); /* 2 bytes for off-by-one errors, just to be safe... */
168 #define READLINE(l, limit) \
169 do { retl = fgets(l, (limit), fdes); \
170 if ((retl == NULL) || (smtp_shutdown == GNUNET_YES)) { \
173 if (core_api->load_monitor != NULL) \
174 GNUNET_network_monitor_notify_transmission (core_api->load_monitor, GNUNET_ND_DOWNLOAD, strlen(retl)); \
178 while (smtp_shutdown == GNUNET_NO)
180 fd = OPEN(pipename, O_RDONLY | O_ASYNC);
183 if (smtp_shutdown == GNUNET_NO)
184 GNUNET_thread_sleep(5 * GNUNET_CRON_SECONDS);
187 fdes = fdopen(fd, "r");
188 while (smtp_shutdown == GNUNET_NO)
190 /* skip until end of header */
193 READLINE(line, linesize);
195 while ((line[0] != '\r') && (line[0] != '\n')); /* expect newline */
196 READLINE(line, linesize); /* read base64 encoded message; decode, process */
200 pos = strlen(line) - 1; /* ignore new line */
201 READLINE(&line[pos], linesize - pos); /* read base64 encoded message; decode, process */
202 if ((line[pos] == '\r') || (line[pos] == '\n'))
203 break; /* empty line => end of message! */
205 size = GNUNET_STRINGS_base64_decode(line, pos, &out);
206 if (size < sizeof(SMTPMessage))
208 GNUNET_GE_BREAK(ectx, 0);
213 mp = (SMTPMessage *)&out[size - sizeof(SMTPMessage)];
214 if (ntohs(mp->header.size) != size)
217 GNUNET_GE_WARNING | GNUNET_GE_BULK | GNUNET_GE_USER,
218 _("Received malformed message via %s. Ignored.\n"),
222 GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER,
223 "Size returned by base64=%d, in the msg=%d.\n", size,
230 stats->change(stat_bytesReceived, size);
231 coreMP = GNUNET_new(GNUNET_TransportPacket);
233 coreMP->size = size - sizeof(SMTPMessage);
234 coreMP->tsession = NULL;
235 coreMP->sender = mp->sender;
237 GNUNET_GE_LOG(ectx, GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER,
238 "SMTP message passed to the core.\n");
241 core_api->receive(coreMP);
245 GNUNET_GE_LOG(ectx, GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER,
246 "SMTP message processed.\n");
255 /* *************** API implementation *************** */
258 * Verify that a hello-Message is correct (a node is reachable at that
259 * address). Since the reply will be asynchronous, a method must be
262 * @param hello the hello message to verify
263 * (the signature/crc have been verified before)
264 * @return GNUNET_OK on success, GNUNET_SYSERR on error
267 api_verify_hello(const GNUNET_MessageHello * hello)
269 const EmailAddress *maddr;
271 maddr = (const EmailAddress *)&hello[1];
272 if ((ntohs(hello->header.size) !=
273 sizeof(GNUNET_MessageHello) + ntohs(hello->senderAddressSize)) ||
274 (maddr->senderAddress
275 [ntohs(hello->senderAddressSize) - 1 - FILTER_STRING_SIZE] != '\0'))
277 GNUNET_GE_BREAK(ectx, 0);
278 return GNUNET_SYSERR; /* obviously invalid */
280 if (NULL == strstr(maddr->filter, ": "))
281 return GNUNET_SYSERR;
286 * Create a hello-Message for the current node. The hello is created
287 * without signature and without a timestamp. The GNUnet core will
288 * GNUNET_RSA_sign the message and add an expiration time.
290 * @return hello on success, NULL on error
292 static GNUNET_MessageHello *
295 GNUNET_MessageHello *msg;
300 GNUNET_GC_get_configuration_value_string(core_api->cfg, "SMTP", "FILTER",
301 "X-mailer: GNUnet", &filter);
302 if (NULL == strstr(filter, ": "))
304 GNUNET_GE_LOG(ectx, GNUNET_GE_WARNING | GNUNET_GE_BULK | GNUNET_GE_USER,
305 _("SMTP filter string to invalid, lacks ': '\n"));
310 if (strlen(filter) > FILTER_STRING_SIZE)
312 filter[FILTER_STRING_SIZE] = '\0';
313 GNUNET_GE_LOG(ectx, GNUNET_GE_WARNING | GNUNET_GE_BULK | GNUNET_GE_USER,
314 _("SMTP filter string to long, capped to `%s'\n"), filter);
316 i = (strlen(email) + 8) & (~7); /* make multiple of 8 */
318 GNUNET_malloc(sizeof(GNUNET_MessageHello) + sizeof(EmailAddress) + i);
319 memset(msg, 0, sizeof(GNUNET_MessageHello) + sizeof(EmailAddress) + i);
320 haddr = (EmailAddress *)&msg[1];
321 memset(&haddr->filter[0], 0, FILTER_STRING_SIZE);
322 strcpy(&haddr->filter[0], filter);
323 GNUNET_memcpy(&haddr->senderAddress[0], email, strlen(email) + 1);
324 msg->senderAddressSize = htons(strlen(email) + 1 + sizeof(EmailAddress));
325 msg->protocol = htons(GNUNET_TRANSPORT_PROTOCOL_NUMBER_SMTP);
326 msg->MTU = htonl(smtpAPI.mtu);
327 msg->header.size = htons(GNUNET_sizeof_hello(msg));
328 if (api_verify_hello(msg) == GNUNET_SYSERR)
329 GNUNET_GE_ASSERT(ectx, 0);
334 struct GetMessageClosure {
341 get_message(void **buf, int *len, void *cls)
343 struct GetMessageClosure *gmc = cls;
351 if (gmc->pos == gmc->esize)
352 return NULL; /* done */
354 gmc->pos = gmc->esize;
359 * Send a message to the specified remote node.
361 * @param tsession the GNUNET_MessageHello identifying the remote node
362 * @param msg what to send
363 * @param size the size of the message
364 * @param important is this message important enough to override typical limits?
365 * @return GNUNET_SYSERR on error, GNUNET_OK on success
368 api_send(GNUNET_TSession * tsession, const void *msg, const unsigned int size,
371 const GNUNET_MessageHello *hello;
372 const EmailAddress *haddr;
377 struct GetMessageClosure gm_cls;
378 smtp_session_t session;
379 smtp_message_t message;
380 smtp_recipient_t recipient;
386 if (smtp_shutdown == GNUNET_YES)
387 return GNUNET_SYSERR;
388 if ((size == 0) || (size > smtpAPI.mtu))
390 GNUNET_GE_BREAK(ectx, 0);
391 return GNUNET_SYSERR;
393 now = GNUNET_get_time();
394 if ((important != GNUNET_YES) &&
395 ((now - last_transmission) * rate_limit) < GNUNET_CRON_HOURS)
396 return GNUNET_NO; /* rate too high */
397 last_transmission = now;
399 hello = (const GNUNET_MessageHello *)tsession->internal;
401 return GNUNET_SYSERR;
402 GNUNET_mutex_lock(lock);
403 session = smtp_create_session();
407 GNUNET_GE_ERROR | GNUNET_GE_ADMIN | GNUNET_GE_USER |
408 GNUNET_GE_IMMEDIATE, _("SMTP: `%s' failed: %s.\n"),
409 "smtp_create_session", smtp_strerror(smtp_errno(), ebuf,
411 GNUNET_mutex_unlock(lock);
412 return GNUNET_SYSERR;
414 if (0 == smtp_set_server(session, smtp_server_name))
417 GNUNET_GE_ERROR | GNUNET_GE_ADMIN | GNUNET_GE_USER |
418 GNUNET_GE_IMMEDIATE, _("SMTP: `%s' failed: %s.\n"),
419 "smtp_set_server", smtp_strerror(smtp_errno(), ebuf,
421 smtp_destroy_session(session);
422 GNUNET_mutex_unlock(lock);
423 return GNUNET_SYSERR;
425 haddr = (const EmailAddress *)&hello[1];
426 message = smtp_add_message(session);
430 GNUNET_GE_WARNING | GNUNET_GE_ADMIN | GNUNET_GE_USER |
431 GNUNET_GE_BULK, _("SMTP: `%s' failed: %s.\n"),
432 "smtp_add_message", smtp_strerror(smtp_errno(), ebuf,
434 smtp_destroy_session(session);
435 GNUNET_mutex_unlock(lock);
436 return GNUNET_SYSERR;
438 smtp_set_header(message, "To", NULL, haddr->senderAddress);
439 smtp_set_header(message, "From", NULL, email);
441 filter = GNUNET_strdup(haddr->filter);
442 fvalue = strstr(filter, ": ");
443 GNUNET_GE_ASSERT(NULL, NULL != fvalue);
446 if (0 == smtp_set_header(message, filter, fvalue))
449 GNUNET_GE_WARNING | GNUNET_GE_ADMIN | GNUNET_GE_USER |
450 GNUNET_GE_BULK, _("SMTP: `%s' failed: %s.\n"),
451 "smtp_set_header", smtp_strerror(smtp_errno(), ebuf,
453 smtp_destroy_session(session);
454 GNUNET_mutex_unlock(lock);
456 return GNUNET_SYSERR;
459 m = GNUNET_malloc(size + sizeof(SMTPMessage));
460 GNUNET_memcpy(m, msg, size);
461 mp = (SMTPMessage *)&m[size];
462 mp->header.size = htons(size + sizeof(SMTPMessage));
463 mp->header.type = htons(0);
464 mp->sender = *core_api->my_identity;
467 gm_cls.esize = GNUNET_STRINGS_base64_encode(m, size + sizeof(SMTPMessage), &gm_cls.ebody);
469 if (0 == smtp_size_set_estimate(message, gm_cls.esize))
472 GNUNET_GE_WARNING | GNUNET_GE_ADMIN | GNUNET_GE_USER |
473 GNUNET_GE_BULK, _("SMTP: `%s' failed: %s.\n"),
474 "smtp_size_set_estimate", smtp_strerror(smtp_errno(), ebuf,
477 if (0 == smtp_set_messagecb(message, &get_message, &gm_cls))
480 GNUNET_GE_WARNING | GNUNET_GE_ADMIN | GNUNET_GE_USER |
481 GNUNET_GE_BULK, _("SMTP: `%s' failed: %s.\n"),
482 "smtp_set_messagecb", smtp_strerror(smtp_errno(), ebuf,
484 smtp_destroy_session(session);
485 GNUNET_mutex_unlock(lock);
486 GNUNET_free(gm_cls.ebody);
487 return GNUNET_SYSERR;
489 recipient = smtp_add_recipient(message, haddr->senderAddress);
490 if (recipient == NULL)
493 GNUNET_GE_WARNING | GNUNET_GE_ADMIN | GNUNET_GE_USER |
494 GNUNET_GE_BULK, _("SMTP: `%s' failed: %s.\n"),
495 "smtp_add_recipient", smtp_strerror(smtp_errno(), ebuf,
497 smtp_destroy_session(session);
498 GNUNET_mutex_unlock(lock);
499 return GNUNET_SYSERR;
501 if (0 == smtp_start_session(session))
504 GNUNET_GE_WARNING | GNUNET_GE_ADMIN | GNUNET_GE_USER |
505 GNUNET_GE_BULK, _("SMTP: `%s' failed: %s.\n"),
506 "smtp_start_session", smtp_strerror(smtp_errno(), ebuf,
508 smtp_destroy_session(session);
509 GNUNET_mutex_unlock(lock);
510 GNUNET_free(gm_cls.ebody);
511 return GNUNET_SYSERR;
514 stats->change(stat_bytesSent, size);
515 if (core_api->load_monitor != NULL)
516 GNUNET_network_monitor_notify_transmission(core_api->load_monitor,
517 GNUNET_ND_UPLOAD, gm_cls.esize);
518 smtp_message_reset_status(message); /* this is needed to plug a 28-byte/message memory leak in libesmtp */
519 smtp_destroy_session(session);
520 GNUNET_mutex_unlock(lock);
521 GNUNET_free(gm_cls.ebody);
526 * Establish a connection to a remote node.
527 * @param hello the hello-Message for the target node
528 * @param tsessionPtr the session handle that is to be set
529 * @param may_reuse can we re-use an existing connection?
530 * @return GNUNET_OK on success, GNUNET_SYSERR if the operation failed
533 api_connect(const GNUNET_MessageHello * hello, GNUNET_TSession ** tsessionPtr,
536 GNUNET_TSession *tsession;
538 tsession = GNUNET_new(GNUNET_TSession);
539 tsession->internal = GNUNET_malloc(GNUNET_sizeof_hello(hello));
540 tsession->peer = hello->senderIdentity;
541 GNUNET_memcpy(tsession->internal, hello, GNUNET_sizeof_hello(hello));
542 tsession->ttype = smtpAPI.protocol_number;
543 (*tsessionPtr) = tsession;
548 * Disconnect from a remote node.
550 * @param tsession the session that is closed
551 * @return GNUNET_OK on success, GNUNET_SYSERR if the operation failed
554 api_disconnect(GNUNET_TSession * tsession)
556 if (tsession != NULL)
558 if (tsession->internal != NULL)
559 GNUNET_free(tsession->internal);
560 GNUNET_free(tsession);
566 * Start the server process to receive inbound traffic.
567 * @return GNUNET_OK on success, GNUNET_SYSERR if the operation failed
570 api_start_transport_server()
572 smtp_shutdown = GNUNET_NO;
573 /* initialize SMTP network */
574 dispatchThread = GNUNET_thread_create(&listenAndDistribute, NULL, 1024 * 4);
575 if (dispatchThread == NULL)
577 GNUNET_GE_DIE_STRERROR(ectx,
578 GNUNET_GE_ADMIN | GNUNET_GE_BULK | GNUNET_GE_FATAL,
580 return GNUNET_SYSERR;
586 * Shutdown the server process (stop receiving inbound traffic). Maybe
590 api_stop_transport_server()
594 smtp_shutdown = GNUNET_YES;
595 GNUNET_thread_stop_sleep(dispatchThread);
596 GNUNET_thread_join(dispatchThread, &unused);
601 * Convert SMTP hello to an IP address (always fails).
604 api_hello_to_address(const GNUNET_MessageHello * hello, void **sa,
605 unsigned int *sa_len)
607 return GNUNET_SYSERR;
614 api_associate(GNUNET_TSession * tsession)
616 return GNUNET_SYSERR; /* SMTP connections can never be associated */
620 * Always succeeds (for now; we should look at adding
621 * frequency limits to SMTP in the future!).
624 api_test_would_try(GNUNET_TSession * tsession, unsigned int size,
627 return GNUNET_OK; /* we always try... */
631 * The exported method. Makes the core api available via a global and
632 * returns the smtp transport API.
634 GNUNET_TransportAPI *
635 inittransport_smtp(struct GNUNET_CoreAPIForTransport * core)
637 unsigned long long mtu;
642 if (!GNUNET_GC_have_configuration_value(core_api->cfg, "SMTP", "EMAIL"))
644 GNUNET_GE_LOG(ectx, GNUNET_GE_ERROR | GNUNET_GE_BULK | GNUNET_GE_USER,
646 ("No email-address specified, can not start SMTP transport.\n"));
649 GNUNET_GC_get_configuration_value_number(core_api->cfg, "SMTP", "MTU", 1200,
651 SMTP_MESSAGE_SIZE, &mtu);
652 GNUNET_GC_get_configuration_value_number(core_api->cfg, "SMTP", "RATELIMIT",
653 0, 0, 1024 * 1024, &rate_limit);
654 stats = core_api->service_request("stats");
658 stats->create(gettext_noop("# bytes received via SMTP"));
659 stat_bytesSent = stats->create(gettext_noop("# bytes sent via SMTP"));
661 stats->create(gettext_noop("# bytes dropped by SMTP (outgoing)"));
663 GNUNET_GC_get_configuration_value_filename(core_api->cfg, "SMTP", "PIPE", &pipename);
665 if (0 != mkfifo(pipename, S_IWUSR | S_IRUSR | S_IWGRP | S_IWOTH))
667 GNUNET_GE_LOG_STRERROR(ectx,
668 GNUNET_GE_ADMIN | GNUNET_GE_BULK | GNUNET_GE_FATAL,
670 GNUNET_free(pipename);
671 core_api->service_release(stats);
675 /* we need to allow the mailer program to send us messages;
676 * easiest done by giving it write permissions (see Mantis #1142) */
677 if (0 != chmod(pipename, S_IWUSR | S_IRUSR | S_IWGRP | S_IWOTH))
678 GNUNET_GE_LOG_STRERROR(ectx,
679 GNUNET_GE_ADMIN | GNUNET_GE_BULK |
680 GNUNET_GE_WARNING, "chmod");
681 GNUNET_GC_get_configuration_value_string(core_api->cfg, "SMTP", "EMAIL", NULL,
683 lock = GNUNET_mutex_create(GNUNET_NO);
684 GNUNET_GC_get_configuration_value_string(core_api->cfg, "SMTP", "SERVER",
685 "localhost:25", &smtp_server_name);
686 sa.sa_handler = SIG_IGN;
687 sigemptyset(&sa.sa_mask);
689 sigaction(SIGPIPE, &sa, &old_handler);
691 smtpAPI.protocol_number = GNUNET_TRANSPORT_PROTOCOL_NUMBER_SMTP;
692 smtpAPI.mtu = mtu - sizeof(SMTPMessage);
694 smtpAPI.hello_verify = &api_verify_hello;
695 smtpAPI.hello_create = &api_create_hello;
696 smtpAPI.connect = &api_connect;
697 smtpAPI.send = &api_send;
698 smtpAPI.associate = &api_associate;
699 smtpAPI.disconnect = &api_disconnect;
700 smtpAPI.server_start = &api_start_transport_server;
701 smtpAPI.server_stop = &api_stop_transport_server;
702 smtpAPI.hello_to_address = &api_hello_to_address;
703 smtpAPI.send_now_test = &api_test_would_try;
710 sigaction(SIGPIPE, &old_handler, NULL);
711 GNUNET_free(smtp_server_name);
714 core_api->service_release(stats);
717 GNUNET_mutex_destroy(lock);
720 GNUNET_free(pipename);