LCOV - code coverage report
Current view: top level - cmail-smtpd - route.c (source / functions) Hit Total Coverage
Test: smtpd.info Lines: 81 112 72.3 %
Date: 2015-11-25 19:06:20 Functions: 3 3 100.0 %

          Line data    Source code
       1             : //this code queries outbound routing information for a user
       2           3 : MAILROUTE route_query(LOGGER log, DATABASE* database, char* user){
       3           3 :         MAILROUTE route = {
       4             :                 .router = NULL,
       5             :                 .argument = NULL
       6             :         };
       7             :         int status;
       8             : 
       9           3 :         if(!user){
      10           0 :                 return route;
      11             :         }
      12             : 
      13           3 :         status = sqlite3_bind_text(database->query_outbound_router, 1, user, -1, SQLITE_STATIC);
      14           3 :         if(status != SQLITE_OK){
      15           0 :                 logprintf(log, LOG_ERROR, "Failed to bind user parameter to routing query: %s\n", sqlite3_errmsg(database->conn));
      16           0 :                 sqlite3_reset(database->query_outbound_router);
      17           0 :                 sqlite3_clear_bindings(database->query_outbound_router);
      18           0 :                 return route;
      19             :         }
      20             : 
      21           3 :         switch(sqlite3_step(database->query_outbound_router)){
      22             :                 case SQLITE_ROW:
      23             :                         //copy data
      24             :                         //This works because router is NOT NULL in schema
      25           3 :                         route.router = common_strdup((char*)sqlite3_column_text(database->query_outbound_router, 0));
      26             : 
      27           3 :                         if(sqlite3_column_text(database->query_outbound_router, 1)){
      28           0 :                                 route.argument = common_strdup((char*)sqlite3_column_text(database->query_outbound_router, 1));
      29             :                         }
      30             : 
      31           3 :                         if(!route.router){
      32           0 :                                 logprintf(log, LOG_ERROR, "Failed to allocate memory for ROUTE structure\n");
      33             :                         }
      34           3 :                         break;
      35             :                 case SQLITE_DONE:
      36           0 :                         logprintf(log, LOG_ERROR, "Database contains no outbound router definition for %s\n", user);
      37           0 :                         break;
      38             :                 default:
      39           0 :                         logprintf(log, LOG_WARNING, "Unhandled query return value: %s\n", sqlite3_errmsg(database->conn));
      40           0 :                         break;
      41             :         }
      42             : 
      43           3 :         sqlite3_reset(database->query_outbound_router);
      44           3 :         sqlite3_clear_bindings(database->query_outbound_router);
      45             : 
      46           3 :         return route;
      47             : }
      48             : 
      49         163 : void route_free(MAILROUTE* route){
      50         163 :         if(route){
      51         163 :                 if(route->router){
      52          48 :                         free(route->router);
      53          48 :                         route->router = NULL;
      54             :                 }
      55             : 
      56         163 :                 if(route->argument){
      57          22 :                         free(route->argument);
      58          22 :                         route->argument = NULL;
      59             :                 }
      60             :         }
      61         163 : }
      62             : 
      63          33 : int route_local_path(LOGGER log, DATABASE* database, MAIL* mail, MAILPATH* current_path){
      64          33 :         int rv = 0;
      65             :         USER_DATABASE* user_db;
      66             :         char path_replacement[SMTP_MAX_PATH_LENGTH];
      67             :         char forward_path[SMTP_MAX_PATH_LENGTH];
      68             : 
      69             :         //reject the path if the path does not have a router table entry
      70             :         //this check should be redundant, as paths without routers are non-local
      71          33 :         if(!current_path->route.router){
      72           0 :                 return -1;
      73             :         }
      74             : 
      75          33 :         logprintf(log, LOG_DEBUG, "Inbound router %s (%s) for %s\n", current_path->route.router, current_path->route.argument ? current_path->route.argument:"none", current_path->path);
      76             : 
      77          33 :         if(!strcmp(current_path->route.router, "store")){
      78          11 :                 if(!(current_path->route.argument)){
      79           2 :                         logprintf(log, LOG_ERROR, "Path is assigned store router without argument, rejecting transaction\n");
      80             :                         //Fail the transaction permanently
      81           2 :                         rv = -1;
      82             :                 }
      83             :                 else{
      84             :                         //preemptively fail the transaction...
      85           9 :                         rv = -1;
      86             : 
      87             :                         //check for user existence (as well as a user database path)
      88           9 :                         if(sqlite3_bind_text(database->query_authdata, 1, current_path->route.argument, -1, SQLITE_STATIC) == SQLITE_OK){
      89           9 :                                 switch(sqlite3_step(database->query_authdata)){
      90             :                                         case SQLITE_ROW:
      91             :                                                 //check for a user database
      92           9 :                                                 if(sqlite3_column_text(database->query_authdata, 2)){
      93           4 :                                                         logprintf(log, LOG_DEBUG, "Fetching user database for user %s (%s)\n", current_path->route.argument, (char*)sqlite3_column_text(database->query_authdata, 2));  
      94           4 :                                                         user_db = database_userdb(log, database, (char*)sqlite3_column_text(database->query_authdata, 2));
      95             : 
      96           4 :                                                         if(!user_db){
      97             :                                                                 //try to refresh the user database set
      98           3 :                                                                 database_refresh(log, database);
      99           3 :                                                                 user_db = database_userdb(log, database, (char*)sqlite3_column_text(database->query_authdata, 2));
     100             : 
     101           3 :                                                                 if(!user_db){
     102             :                                                                         //as last resort, store to master db
     103           1 :                                                                         logprintf(log, LOG_WARNING, "Stored mail for user %s to master instead of defined database\n", current_path->route.argument);
     104           1 :                                                                         rv = mail_store_inbox(log, database->mail_storage.mailbox_master, mail, current_path);
     105             :                                                                 }
     106             :                                                                 else{
     107           2 :                                                                         logprintf(log, LOG_DEBUG, "Storing mail for %s to user database %s after database refresh\n", current_path->route.argument, user_db->file_name);
     108           2 :                                                                         rv = mail_store_inbox(log, user_db->mailbox, mail, current_path);
     109             :                                                                 }
     110             :                                                         }
     111             :                                                         else{
     112           1 :                                                                 logprintf(log, LOG_DEBUG, "Storing mail for %s to user database %s\n", current_path->route.argument, user_db->file_name);
     113           1 :                                                                 rv = mail_store_inbox(log, user_db->mailbox, mail, current_path);
     114             :                                                         }
     115             : 
     116             :                                                 }
     117             :                                                 else{
     118             :                                                         //simply store to master
     119           5 :                                                         logprintf(log, LOG_DEBUG, "Storing mail for %s to master database\n", current_path->route.argument);
     120           5 :                                                         rv = mail_store_inbox(log, database->mail_storage.mailbox_master, mail, current_path);
     121             :                                                 }
     122           9 :                                                 break;
     123             :                                         case SQLITE_DONE:
     124           0 :                                                 logprintf(log, LOG_ERROR, "Failed to resolve router argument %s to a user, failing transaction\n", current_path->route.argument);
     125           0 :                                                 break;
     126             :                                         default:
     127           0 :                                                 logprintf(log, LOG_ERROR, "Failed to execute user info query: %s\n", sqlite3_errmsg(database->conn));
     128           0 :                                                 break;
     129             :                                 }
     130             :                         }
     131             :                         else{
     132           0 :                                 logprintf(log, LOG_ERROR, "Failed to bind user to user info query\n");
     133             :                         }
     134             : 
     135           9 :                         sqlite3_reset(database->query_authdata);
     136           9 :                         sqlite3_clear_bindings(database->query_authdata);
     137             : 
     138             :                         //if we could not store mail, retry with master
     139           9 :                         if(rv){
     140           0 :                                 logprintf(log, LOG_ERROR, "Failed to store mail for %s (%d: %s), retrying one last time with master db\n", current_path->route.argument, rv, sqlite3_errmsg(database->conn));
     141           0 :                                 rv = mail_store_inbox(log, database->mail_storage.mailbox_master, mail, current_path);
     142           0 :                                 if(rv){
     143           0 :                                         logprintf(log, LOG_ERROR, "Failed to store mail: %s\n", sqlite3_errmsg(database->conn));
     144             :                                 }
     145             :                         }
     146             :                 }
     147             :         }
     148          22 :         else if(!strcmp(current_path->route.router, "redirect")){
     149          10 :                 if(current_path->route.argument){
     150             :                         //FIXME this should be ensured to be properly terminated
     151           8 :                         strncpy(forward_path, current_path->route.argument, sizeof(forward_path) - 1);
     152             : 
     153             :                         //mangle the envelope recipient according to the route
     154             :                         //we're able to do this and not worry about exploitation from unsanitized input
     155             :                         //because the path parser removes all comments from user-supplied paths
     156           8 :                         strncpy(path_replacement, current_path->path, current_path->delimiter_position);
     157           8 :                         path_replacement[current_path->delimiter_position] = 0;
     158           8 :                         if(common_strrepl(forward_path, sizeof(forward_path), "(to-local)", path_replacement) < 0){
     159           0 :                                 logprintf(log, LOG_ERROR, "Failed to replace to-local variable in redirect router\n");
     160             :                                 //fail the transaction
     161           0 :                                 rv = -1;
     162             :                         }
     163             : 
     164          16 :                         if(!rv &&
     165           8 :                                 common_strrepl(forward_path, sizeof(forward_path), "(to-domain)",
     166             :                                 //this ensures that the variable always gets replaced, with an empty string if need be
     167           8 :                                 current_path->path + current_path->delimiter_position + (current_path->path[current_path->delimiter_position] ? 1:0) ) < 0){
     168           0 :                                 logprintf(log, LOG_ERROR, "Failed to replace to-domain variable in redirect router\n");
     169             :                                 //fail the transaction
     170           0 :                                 rv = -1;
     171             :                         }
     172             : 
     173           8 :                         if(!rv){
     174           8 :                                 strncpy(path_replacement, mail->reverse_path.path, mail->reverse_path.delimiter_position);
     175           8 :                                 path_replacement[mail->reverse_path.delimiter_position] = 0;
     176           8 :                                 if(common_strrepl(forward_path, sizeof(forward_path), "(from-local)", path_replacement) < 0){
     177           0 :                                         logprintf(log, LOG_ERROR, "Failed to replace from-local variable in redirect router\n");
     178             :                                         //fail the transaction
     179           0 :                                         rv = -1;
     180             :                                 }
     181             :                         }
     182             : 
     183          16 :                         if(!rv &&
     184           8 :                                 common_strrepl(forward_path, sizeof(forward_path), "(from-domain)",
     185           8 :                                 mail->reverse_path.path + mail->reverse_path.delimiter_position + (current_path->path[current_path->delimiter_position] ? 1:0)) < 0){
     186           0 :                                 logprintf(log, LOG_ERROR, "Failed to replace from-domain variable in redirect router\n");
     187             :                                 //fail the transaction
     188           0 :                                 rv = -1;
     189             :                         }
     190             : 
     191             :                         //insert into outbound table
     192           8 :                         if(!rv){
     193           8 :                                 rv = mail_store_outbox(log, database->mail_storage.outbox_master, NULL, forward_path, mail);
     194             :                         }
     195             :                 }
     196             :                 else{
     197           2 :                         logprintf(log, LOG_ERROR, "Redirect router without argument, failing transaction\n");
     198             :                         //fail the transaction permanently
     199           2 :                         rv = -1;
     200             :                 }
     201             :         }
     202          12 :         else if(!strcmp(current_path->route.router, "handoff")){
     203           4 :                 if(current_path->route.argument){
     204             :                         //insert into outbound table
     205           2 :                         rv = mail_store_outbox(log, database->mail_storage.outbox_master, current_path->route.argument, current_path->path, mail);
     206             :                 }
     207             :                 else{
     208           2 :                         logprintf(log, LOG_ERROR, "Handoff router without argument, failing transaction\n");
     209           2 :                         rv = -1;
     210             :                 }
     211             :         }
     212           8 :         else if(!strcmp(current_path->route.router, "reject")){
     213             :                 //this should probably never be reached as the path
     214             :                 //of the user should already have been rejected.
     215             :                 //otherwise, a mail with multiple recipients including
     216             :                 //this one might get bounced on the basis of one
     217             :                 //rejecting recipient
     218           0 :                 rv = -1;
     219             :         }
     220           8 :         else if(!strcmp(current_path->route.router, "drop")){
     221             :                 //this one is easy.
     222             :         }
     223             :         else{
     224             :                 //TODO call plugins for other routers
     225             :         }
     226             : 
     227          33 :         if(rv>0){
     228           0 :                 logprintf(log, LOG_INFO, "Additional information: %s\n", sqlite3_errmsg(database->conn));
     229             :         }
     230             : 
     231          33 :         return rv;
     232             : }

Generated by: LCOV version 1.11