LCOV - code coverage report
Current view: top level - cmail-smtpd - mail.c (source / functions) Hit Total Coverage
Test: smtpd.info Lines: 100 146 68.5 %
Date: 2015-11-25 19:06:20 Functions: 7 7 100.0 %

          Line data    Source code
       1          31 : int mail_route(LOGGER log, MAIL* mail, DATABASE* database){
       2             :         unsigned i;
       3          31 :         unsigned rv = 250;
       4             :         struct timespec time_spec;
       5             : 
       6             :         //generate a message id
       7          31 :         if(clock_gettime(CLOCK_REALTIME_COARSE, &time_spec) < 0){
       8           0 :                 logprintf(log, LOG_ERROR, "Failed to get time for message id generation: %s\n", strerror(errno));
       9           0 :                 time_spec.tv_sec = time(NULL);
      10             :         }
      11             : 
      12          31 :         snprintf(mail->message_id, CMAIL_MESSAGEID_MAX, "%X%X.%X-%s", (unsigned)time_spec.tv_sec, (unsigned)time_spec.tv_nsec, rand(), mail->submitter);
      13          31 :         logprintf(log, LOG_INFO, "Generated message ID %s\n", mail->message_id);
      14             : 
      15             :         //iterate over recipients
      16          58 :         for(i = 0; mail->forward_paths[i]; i++){
      17          33 :                 logprintf(log, LOG_DEBUG, "Routing forward path %d: %s (%s)\n", i, mail->forward_paths[i]->path, mail->forward_paths[i]->route.router ? (mail->forward_paths[i]->route.router):"outbound");
      18          33 :                 if(mail->forward_paths[i]->route.router){
      19             :                         //inbound mail, apply inrouter
      20             :                         //TODO RFC5321 4.4 (P59) says partial accepted recipient list should 200 and send failure notifications
      21          33 :                         switch(route_local_path(log, database, mail, mail->forward_paths[i])){
      22             :                                 case 0:
      23          27 :                                         break;
      24             :                                 case 1:
      25           0 :                                         logprintf(log, LOG_WARNING, "Failed to store inbound entry for path %d (%s), deferring transaction\n", i, mail->forward_paths[i]->path);
      26           0 :                                         return 400;
      27             :                                 default:
      28           6 :                                         logprintf(log, LOG_WARNING, "Failed to route path %d (%s) inbound, rejecting transaction\n", i, mail->forward_paths[i]->path);
      29           6 :                                         return 500;
      30             :                         }
      31             :                 }
      32             :                 else{
      33             :                         //outbound mail, should have been authenticated so accept it automatically
      34           0 :                         switch(mail_store_outbox(log, database->mail_storage.outbox_master, NULL, mail->forward_paths[i]->path, mail)){
      35             :                                 case 0:
      36           0 :                                         break;
      37             :                                 case 1:
      38           0 :                                         logprintf(log, LOG_WARNING, "Failed to store outbound entry for path %d (%s), deferring transaction\n", i, mail->forward_paths[i]->path);
      39           0 :                                         return 400;
      40             :                                 default:
      41           0 :                                         logprintf(log, LOG_WARNING, "Failed to route path %d (%s) outbound, rejecting transaction\n", i, mail->forward_paths[i]->path);
      42           0 :                                         return 500;
      43             :                         }
      44             :                 }
      45             :         }
      46             : 
      47          25 :         return rv;
      48             : }
      49             : 
      50          10 : int mail_originate(LOGGER log, char* user, MAIL* mail, MAILROUTE route, DATABASE* database){
      51          10 :         int rv = 250, i;
      52             : 
      53             :         //user has no routing entry, reject the mail
      54             :         //this should already have happened early in the conversation
      55          10 :         if(!route.router){
      56           0 :                 return 500;
      57             :         }
      58             : 
      59          10 :         logprintf(log, LOG_INFO, "Outbound router for connected user %s is %s (%s)\n", user, route.router, route.argument?route.argument:"none");
      60             : 
      61          10 :         if(!strcmp(route.router, "drop")){
      62             :                 //done.
      63             :         }
      64          10 :         else if(!strcmp(route.router, "handoff")){
      65           0 :                 if(route.argument){
      66           0 :                         for(i = 0; mail->forward_paths[i]; i++){
      67             :                                 //insert into outbound table
      68           0 :                                 logprintf(log, LOG_DEBUG, "Handing off path %d: %s\n", i, mail->forward_paths[i]->path);
      69           0 :                                 switch(mail_store_outbox(log, database->mail_storage.outbox_master, route.argument, mail->forward_paths[i]->path, mail)){
      70             :                                         case 0:
      71           0 :                                                 break;
      72             :                                         case 1:
      73           0 :                                                 logprintf(log, LOG_WARNING, "Failed to store handoff entry for path %d (%s), deferring transaction\n", i, mail->forward_paths[i]->path);
      74           0 :                                                 return 400;
      75             :                                         default:
      76           0 :                                                 logprintf(log, LOG_WARNING, "Failed to route path %d (%s) via handoff, rejecting transaction\n", i, mail->forward_paths[i]->path);
      77           0 :                                                 return 500;
      78             :                                 }
      79             :                         }
      80             :                 }
      81             :         }
      82             :         else{
      83          10 :                 rv = mail_route(log, mail, database);
      84             :         }
      85             : 
      86             :         //may check for invalid routers here
      87             : 
      88          10 :         return rv;
      89             : }
      90             : 
      91         131 : int mail_line(LOGGER log, MAIL* mail, char* line){
      92             :         //logprintf(log, LOG_DEBUG, "Mail line is \"%s\"\n", line);
      93         131 :         if(mail->data_max>0 && mail->data_offset >= mail->data_max){
      94           0 :                 logprintf(log, LOG_INFO, "Mail length (%d) exceeded data length limit (%d), truncating\n", mail->data_offset, mail->data_max);
      95           0 :                 return -1;
      96             :         }
      97             : 
      98         131 :         if(!mail->data || mail->data_allocated < mail->data_offset+strlen(line) + 3){
      99          18 :                 mail->data = realloc(mail->data, mail->data_allocated + strlen(line) + 3);
     100          18 :                 if(!mail->data){
     101           0 :                         logprintf(log, LOG_ERROR, "Failed to reallocate mail buffer\n");
     102           0 :                         return -1;
     103             :                 }
     104          18 :                 mail->data_allocated += strlen(line) + 3;
     105          18 :                 logprintf(log, LOG_DEBUG, "Reallocated mail data buffer to %d bytes\n", mail->data_allocated);
     106             :         }
     107             : 
     108         131 :         if(mail->data_offset != 0){
     109             :                 //insert crlf
     110         100 :                 logprintf(log, LOG_DEBUG, "Inserting newline into data buffer\n");
     111         100 :                 mail->data[mail->data_offset++] = '\r';
     112         100 :                 mail->data[mail->data_offset++] = '\n';
     113             :         }
     114             : 
     115             :         //mind the terminator
     116         131 :         logprintf(log, LOG_DEBUG, "Adding %d bytes to mail at index %d\n", strlen(line), mail->data_offset);
     117         131 :         strncpy(mail->data + mail->data_offset, line, strlen(line) + 1);
     118         131 :         mail->data_offset += strlen(line);
     119         131 :         return 0;
     120             : }
     121             : 
     122          31 : int mail_recvheader(LOGGER log, MAIL* mail, char* announce, bool suppress_submitter){
     123             :         char time_buffer[SMTP_HEADER_LINE_MAX];
     124          31 :         char* recv_header = NULL;
     125          31 :         unsigned header_allocated = 0;
     126             : 
     127          31 :         unsigned mark=0, i, off=0;
     128             : 
     129          31 :         MAILPATH* forward_path = (mail->forward_paths[1]) ? NULL:mail->forward_paths[0];
     130             : 
     131             :         //create timestring
     132          31 :         if(common_tprintf("%a, %d %b %Y %T %z", time(NULL), time_buffer, sizeof(time_buffer) - 1) < 0){
     133           0 :                 logprintf(log, LOG_ERROR, "Failed to get current time, buffer length probably not suitable\n");
     134           0 :                 snprintf(time_buffer, sizeof(time_buffer)-1, "Time failed");
     135             :         }
     136             : 
     137             :         //write received: header
     138          31 :         recv_header = common_strappf(recv_header, &header_allocated,
     139             :                         "Received: from %s by %s%s%s with %s; %s",
     140             :                         suppress_submitter ? "remote":mail->submitter,
     141             :                         announce,
     142             :                         forward_path ? " for ":"",
     143             :                         forward_path ? forward_path->path:"",
     144             :                         mail->protocol,
     145             :                         time_buffer);
     146             : 
     147          31 :         if(!recv_header){
     148           0 :                 logprintf(log, LOG_ERROR, "Failed to allocate memory for Received: header\n");
     149           0 :                 return -1;
     150             :         }
     151             : 
     152          31 :         logprintf(log, LOG_DEBUG, "%d bytes of header data: %s\n", header_allocated, recv_header);
     153             : 
     154        2989 :         for(i = 0; i <= header_allocated; i++){
     155        2989 :                 if(recv_header[i] == ' '){
     156         430 :                         mark = i;
     157             :                 }
     158             : 
     159        2989 :                 if(((i - off) >= SMTP_HEADER_LINE_MAX && off < mark) || recv_header[i] == 0){
     160             :                         //add current contents
     161             :                         //terminate
     162          60 :                         if(recv_header[i]){
     163          29 :                                 recv_header[mark] = 0;
     164             :                         }
     165          60 :                         mail_line(log, mail, recv_header + off);
     166             :                         //un-terminate
     167          60 :                         recv_header[mark] = ' ';
     168             : 
     169          60 :                         off = mark;
     170          60 :                         if(recv_header[i] == 0){
     171          31 :                                 break;
     172             :                         }
     173             :                 }
     174             :         }
     175             : 
     176          31 :         free(recv_header);
     177          31 :         return 0;
     178             : }
     179             : 
     180          86 : int mail_reset(MAIL* mail){
     181             :         unsigned i;
     182             : 
     183          86 :         if(!mail){
     184           0 :                 return -1;
     185             :         }
     186             : 
     187             :         //changes made here must be reflected in client_accept
     188         172 :         MAIL empty_mail = {
     189             :                 .reverse_path = {
     190             :                         .delimiter_position = 0,
     191             :                         .in_transaction = false,
     192             :                         .path = "",
     193             :                         .route = {
     194             :                                 .router = NULL,
     195             :                                 .argument = NULL
     196             :                         }
     197             :                 },
     198             :                 .forward_paths = {
     199             :                         NULL
     200             :                 },
     201             :                 .data_offset = 0,
     202             :                 .data_allocated = 0,
     203             :                 .data = NULL,
     204             :                 .submitter = NULL,
     205             :                 //FIXME this might pose a security risk, setting to UNKNOWN might be better.
     206             :                 //Setting to NULL breaks the constraint when inserting
     207          86 :                 .protocol = mail->protocol,
     208             :                 .message_id = "",
     209             :                 .hop_count = 0,
     210             :                 .header_offset = 0
     211             :         };
     212             : 
     213          86 :         empty_mail.data_allocated = mail->data_allocated;
     214          86 :         empty_mail.data = mail->data;
     215             :         //Keep submitter pointing to the submitter of the CLIENT structure
     216          86 :         empty_mail.submitter = mail->submitter;
     217          86 :         path_reset(&(mail->reverse_path));
     218             : 
     219          86 :         if(mail->data){
     220          71 :                 mail->data[0] = 0;
     221             :         }
     222             : 
     223         125 :         for(i = 0; i < SMTP_MAX_RECIPIENTS && mail->forward_paths[i]; i++){
     224          39 :                 pathpool_return(mail->forward_paths[i]);
     225             :         }
     226             : 
     227          86 :         *mail = empty_mail;
     228          86 :         return 0;
     229             : }
     230             : 
     231           9 : int mail_store_inbox(LOGGER log, sqlite3_stmt* stmt, MAIL* mail, MAILPATH* current_path){
     232             :         //calling contract: 0 -> ok, -1 -> fail, 1 -> defer
     233             :         int status;
     234             : 
     235           9 :         if(sqlite3_bind_text(stmt, 1, current_path->route.argument, -1, SQLITE_STATIC) != SQLITE_OK
     236           9 :                 || sqlite3_bind_text(stmt, 2, mail->message_id, -1, SQLITE_STATIC) != SQLITE_OK
     237           9 :                 || sqlite3_bind_text(stmt, 3, current_path->path, -1, SQLITE_STATIC) != SQLITE_OK
     238           9 :                 || sqlite3_bind_text(stmt, 4, mail->reverse_path.path, -1, SQLITE_STATIC) != SQLITE_OK
     239           9 :                 || sqlite3_bind_text(stmt, 5, mail->submitter, -1, SQLITE_STATIC) != SQLITE_OK
     240           9 :                 || sqlite3_bind_text(stmt, 6, mail->protocol, -1, SQLITE_STATIC) != SQLITE_OK
     241           9 :                 || sqlite3_bind_text(stmt, 7, mail->data, -1, SQLITE_STATIC) != SQLITE_OK){
     242           0 :                 logprintf(log, LOG_ERROR, "Failed to bind mail storage parameter\n");
     243           0 :                 sqlite3_reset(stmt);
     244           0 :                 sqlite3_clear_bindings(stmt);
     245           0 :                 return -1;
     246             :         }
     247             : 
     248           9 :         status = sqlite3_step(stmt);
     249           9 :         switch(status){
     250             :                 case SQLITE_DONE:
     251           9 :                         status = 0;
     252           9 :                         break;
     253             :                 case SQLITE_TOOBIG:
     254             :                 case SQLITE_CONSTRAINT:
     255             :                 case SQLITE_RANGE:
     256           0 :                         status = -1;
     257           0 :                         break;
     258             :                 default:
     259           0 :                         logprintf(log, LOG_INFO, "Unhandled return value from insert statement: %d\n", status);
     260           0 :                         status = 1;
     261             :         }
     262             : 
     263           9 :         sqlite3_reset(stmt);
     264           9 :         sqlite3_clear_bindings(stmt);
     265           9 :         return status;
     266             : }
     267             : 
     268          10 : int mail_store_outbox(LOGGER log, sqlite3_stmt* stmt, char* mail_remote, char* envelope_to, MAIL* mail){
     269             :         //calling contract: 0 -> ok, -1 -> fail, 1 -> defer
     270             :         int status;
     271             : 
     272          10 :         if(sqlite3_bind_text(stmt, 1, mail_remote, -1, SQLITE_STATIC) != SQLITE_OK
     273          10 :                 || sqlite3_bind_text(stmt, 2, mail->reverse_path.path, -1, SQLITE_STATIC) != SQLITE_OK
     274          10 :                 || sqlite3_bind_text(stmt, 3, envelope_to, -1, SQLITE_STATIC) != SQLITE_OK
     275          10 :                 || sqlite3_bind_text(stmt, 4, mail->submitter, -1, SQLITE_STATIC) != SQLITE_OK
     276          10 :                 || sqlite3_bind_text(stmt, 5, mail->data, -1, SQLITE_STATIC) != SQLITE_OK){
     277           0 :                 logprintf(log, LOG_ERROR, "Failed to bind mail storage parameter\n");
     278           0 :                 sqlite3_reset(stmt);
     279           0 :                 sqlite3_clear_bindings(stmt);
     280           0 :                 return -1;
     281             :         }
     282             : 
     283          10 :         status = sqlite3_step(stmt);
     284          10 :         switch(status){
     285             :                 case SQLITE_DONE:
     286          10 :                         status = 0;
     287          10 :                         break;
     288             :                 case SQLITE_TOOBIG:
     289             :                 case SQLITE_CONSTRAINT:
     290             :                 case SQLITE_RANGE:
     291           0 :                         status = -1;
     292           0 :                         break;
     293             :                 default:
     294           0 :                         logprintf(log, LOG_INFO, "Unhandled return value from insert statement: %d\n", status);
     295           0 :                         status = 1;
     296           0 :                         break;
     297             :         }
     298             : 
     299          10 :         sqlite3_reset(stmt);
     300          10 :         sqlite3_clear_bindings(stmt);
     301          10 :         return status;
     302             : }

Generated by: LCOV version 1.11