LCOV - code coverage report
Current view: top level - cmail-smtpd - smtpstatemachine.c (source / functions) Hit Total Coverage
Test: smtpd.info Lines: 261 339 77.0 %
Date: 2015-11-25 19:06:20 Functions: 5 5 100.0 %

          Line data    Source code
       1          21 : int smtpstate_new(LOGGER log, CONNECTION* client, DATABASE* database, PATHPOOL* path_pool){
       2          21 :         CLIENT* client_data=(CLIENT*)client->aux_data;
       3          21 :         LISTENER* listener_data=(LISTENER*)client_data->listener->aux_data;
       4             : 
       5          21 :         if(!strncasecmp(client_data->recv_buffer, "ehlo ", 5)){
       6             :                 #ifndef CMAIL_NO_TLS
       7          18 :                 switch(client_data->listener->tls_mode){
       8             :                         case TLS_ONLY:
       9           1 :                                 client_data->current_mail.protocol=(client_data->sasl_user.authenticated?"sesmtpa":"sesmtp");
      10           1 :                                 break;
      11             :                         case TLS_NEGOTIATE:
      12          17 :                                 if(client->tls_mode==TLS_ONLY){
      13           4 :                                         client_data->current_mail.protocol=(client_data->sasl_user.authenticated?"esmtpsa":"esmtps");
      14           4 :                                         break;
      15             :                                 }
      16             :                         case TLS_NONE:
      17          13 :                                 client_data->current_mail.protocol=(client_data->sasl_user.authenticated?"esmtpa":"esmtp");
      18          13 :                                 break;
      19             :                 }
      20             :                 #else
      21             :                 client_data->current_mail.protocol=(client_data->sasl_user.authenticated?"esmtpa":"esmtp");
      22             :                 #endif
      23             : 
      24          18 :                 client_data->state=STATE_IDLE;
      25             : 
      26          18 :                 client_send(log, client, "250-%s ahoyhoy\r\n", listener_data->announce_domain);
      27             : 
      28             :                 //TODO hook plugins here
      29             : 
      30             :                 //send hardcoded esmtp options
      31          18 :                 client_send(log, client, "250-SIZE %d\r\n", listener_data->max_size);
      32          18 :                 client_send(log, client, "250-8BITMIME\r\n"); //FIXME this might imply more processing than planned
      33          18 :                 client_send(log, client, "250-SMTPUTF8\r\n"); //RFC 6531
      34          18 :                 switch(listener_data->auth_offer){
      35             :                         case AUTH_NONE:
      36          10 :                                 break;
      37             :                         case AUTH_TLSONLY:
      38             :                                 #ifndef CMAIL_NO_TLS
      39           8 :                                 if(client->tls_mode!=TLS_ONLY){
      40           4 :                                         break;
      41             :                                 }
      42             :                                 #else
      43             :                                 break;
      44             :                                 #endif
      45             :                         case AUTH_ANY:
      46           4 :                                 client_send(log, client, "250-AUTH PLAIN\r\n");
      47           4 :                                 break;
      48             :                 }
      49             :                 #ifndef CMAIL_NO_TLS
      50             :                 //advertise only when possible
      51          18 :                 if(client_data->listener->tls_mode==TLS_NEGOTIATE && client->tls_mode==TLS_NONE){
      52          13 :                         client_send(log, client, "250-STARTTLS\r\n"); //RFC 3207
      53             :                 }
      54             :                 #endif
      55          18 :                 client_send(log, client, "250 XYZZY\r\n"); //RFC 5321 2.2.2
      56          18 :                 return 0;
      57             :         }
      58             : 
      59           3 :         if(!strncasecmp(client_data->recv_buffer, "helo ", 5)){
      60             :                 #ifndef CMAIL_NO_TLS
      61           2 :                 switch(client_data->listener->tls_mode){
      62             :                         case TLS_ONLY:
      63           0 :                                 client_data->current_mail.protocol = (client_data->sasl_user.authenticated ? "ssmtpa":"ssmtp");
      64           0 :                                 break;
      65             :                         case TLS_NEGOTIATE:
      66           2 :                                 if(client->tls_mode==TLS_ONLY){
      67           1 :                                         client_data->current_mail.protocol = (client_data->sasl_user.authenticated ? "smtpsa":"smtps");
      68           1 :                                         break;
      69             :                                 }
      70             :                         case TLS_NONE:
      71           1 :                                 client_data->current_mail.protocol = (client_data->sasl_user.authenticated ? "smtpa":"smtp");
      72           1 :                                 break;
      73             :                 }
      74             :                 #else
      75             :                 client_data->current_mail.protocol = (client_data->sasl_user.authenticated ? "smtpa":"smtp");
      76             :                 #endif
      77             : 
      78           2 :                 client_data->state=STATE_IDLE;
      79           2 :                 logprintf(log, LOG_INFO, "Client negotiates smtp\n");
      80             : 
      81           2 :                 client_send(log, client, "250 %s ahoyhoy\r\n", listener_data->announce_domain);
      82           2 :                 return 0;
      83             :         }
      84             : 
      85           1 :         logprintf(log, LOG_INFO, "Command not recognized in state NEW: %s\n", client_data->recv_buffer);
      86           1 :         client_send(log, client, "500 Unknown command\r\n");
      87           1 :         return -1;
      88             : }
      89             : 
      90          14 : int smtpstate_auth(LOGGER log, CONNECTION* client, DATABASE* database, PATHPOOL* path_pool){
      91          14 :         CLIENT* client_data = (CLIENT*)client->aux_data;
      92          14 :         int status = SASL_ERROR_PROCESSING;
      93          14 :         char* method = NULL;
      94          14 :         char* parameter = NULL;
      95          14 :         char* challenge = NULL;
      96             : 
      97          14 :         if(client_data->sasl_context.method != SASL_INVALID && !strcmp(client_data->recv_buffer, "*")){
      98             :                 //cancel authentication
      99           1 :                 logprintf(log, LOG_INFO, "Client cancelled authentication\n");
     100           1 :                 client_data->state=STATE_IDLE;
     101           1 :                 sasl_cancel(&(client_data->sasl_context));
     102           1 :                 client_send(log, client, "501 Authentication cancelled\r\n");
     103           1 :                 return 0;
     104             :         }
     105          13 :         else if(client_data->sasl_context.method != SASL_INVALID){
     106           2 :                 status = sasl_continue(log, &(client_data->sasl_context), client_data->recv_buffer, &challenge);
     107             :         }
     108          11 :         else if(!strncasecmp(client_data->recv_buffer, "auth ", 5)){
     109             :                 //tokenize along spaces
     110          11 :                 method = strtok(client_data->recv_buffer + 5, " ");
     111          11 :                 if(method){
     112          10 :                         parameter = strtok(NULL, " ");
     113          10 :                         logprintf(log, LOG_DEBUG, "Beginning SASL with method %s parameter %s\n", method, parameter?parameter:"null");
     114          10 :                         status = sasl_begin(log, &(client_data->sasl_context), &(client_data->sasl_user), method, parameter, &challenge);
     115             :                 }
     116             :                 else{
     117           1 :                         logprintf(log, LOG_WARNING, "Client tried auth without supplying method\n");
     118           1 :                         status = SASL_UNKNOWN_METHOD;
     119             :                 }
     120             :         }
     121             :         else{
     122           0 :                 logprintf(log, LOG_ERROR, "Invalid state (No negotiation but data) reached in AUTH, returning to IDLE\r\n");
     123           0 :                 client_data->state=STATE_IDLE;
     124           0 :                 client_send(log, client, "500 Invalid state\r\n");
     125           0 :                 return -1;
     126             :         }
     127             : 
     128          13 :         switch(status){
     129             :                 case SASL_OK:
     130           0 :                         logprintf(log, LOG_ERROR, "Invalid state (SASL_OK) reached in AUTH, returning to IDLE\r\n");
     131             :                         //FIXME might want to reset sasl_user here
     132           0 :                         sasl_reset_ctx(&(client_data->sasl_context), false); //opting for security here, rather than freeing memory we do not own
     133           0 :                         client_data->state = STATE_IDLE;
     134           0 :                         client_send(log, client, "500 Invalid state\r\n");
     135           0 :                         return -1;
     136             :                 case SASL_ERROR_PROCESSING:
     137           0 :                         logprintf(log, LOG_ERROR, "SASL processing error\r\n");
     138           0 :                         sasl_reset_ctx(&(client_data->sasl_context), true);
     139           0 :                         client_send(log, client, "454 Internal Error\r\n");
     140           0 :                         client_data->state = STATE_IDLE;
     141           0 :                         return 0;
     142             :                 case SASL_ERROR_DATA:
     143           2 :                         logprintf(log, LOG_ERROR, "SASL failed to parse data\r\n");
     144           2 :                         sasl_reset_ctx(&(client_data->sasl_context), true);
     145           2 :                         client_send(log, client, "501 Invalid data provided\r\n");
     146           2 :                         client_data->state = STATE_IDLE;
     147           2 :                         return -1;
     148             :                 case SASL_UNKNOWN_METHOD:
     149           3 :                         logprintf(log, LOG_WARNING, "Client tried unsupported authentication method: %s\n", client_data->recv_buffer + 5);
     150           3 :                         client_data->state = STATE_IDLE;
     151           3 :                         sasl_reset_ctx(&(client_data->sasl_context), false);
     152           3 :                         client_send(log, client, "504 Unknown mechanism\r\n");
     153           3 :                         return -1;
     154             :                 case SASL_CONTINUE:
     155           3 :                         logprintf(log, LOG_INFO, "Asking for SASL continuation\r\n");
     156           3 :                         client_send(log, client, "334 %s\r\n", challenge ? challenge:"");
     157           3 :                         return 0;
     158             :                 case SASL_DATA_OK:
     159           5 :                         sasl_reset_ctx(&(client_data->sasl_context), true);
     160           5 :                         client_data->state = STATE_IDLE;
     161             : 
     162             :                         //check auth data
     163           5 :                         if(!challenge || auth_validate(log, database, client_data->sasl_user.authenticated, challenge, &(client_data->sasl_user.authorized)) < 0){
     164             :                                 //login failed
     165           2 :                                 sasl_reset_user(&(client_data->sasl_user), true);
     166           2 :                                 logprintf(log, LOG_INFO, "Client failed to authenticate\n");
     167           2 :                                 client_send(log, client, "535 Authentication failed\r\n");
     168             : 
     169             :                                 //increase the failscore
     170           2 :                                 return -1;
     171             :                         }
     172             : 
     173           3 :                         if(!client_data->sasl_user.authorized){
     174           0 :                                 sasl_reset_user(&(client_data->sasl_user), true);
     175           0 :                                 logprintf(log, LOG_ERROR, "Failed to allocate memory for authorized user\n");
     176           0 :                                 client_send(log, client, "454 Internal Error\r\n");
     177           0 :                                 return 0;
     178             :                         }
     179             : 
     180           3 :                         client_data->originating_route = route_query(log, database, client_data->sasl_user.authorized);
     181             : 
     182           3 :                         logprintf(log, LOG_INFO, "Client authenticated as %s\n", client_data->sasl_user.authenticated);
     183           3 :                         client_send(log, client, "235 Authenticated\r\n");
     184             : 
     185             :                         //update the protocol version
     186             :                         #ifndef CMAIL_NO_TLS
     187           3 :                         switch(client_data->listener->tls_mode){
     188             :                                 case TLS_ONLY:
     189           0 :                                         client_data->current_mail.protocol = "sesmtpa";
     190           0 :                                         break;
     191             :                                 case TLS_NEGOTIATE:
     192           3 :                                         if(client->tls_mode == TLS_ONLY){
     193           3 :                                                 client_data->current_mail.protocol = "esmtpsa";
     194           3 :                                                 break;
     195             :                                         }
     196             :                                 case TLS_NONE:
     197           0 :                                         client_data->current_mail.protocol = "esmtpa";
     198           0 :                                         break;
     199             :                         }
     200             :                         #else
     201             :                         client_data->current_mail.protocol = "esmtpa";
     202             :                         #endif
     203             : 
     204             :                         //decrease the failscore
     205           3 :                         return 1;
     206             :         }
     207             : 
     208           0 :         logprintf(log, LOG_ERROR, "Invalid branch reached in AUTH\n");
     209           0 :         return -1;
     210             : }
     211             : 
     212         102 : int smtpstate_idle(LOGGER log, CONNECTION* client, DATABASE* database, PATHPOOL* path_pool){
     213         102 :         CLIENT* client_data = (CLIENT*)client->aux_data;
     214         102 :         LISTENER* listener_data = (LISTENER*)client_data->listener->aux_data;
     215             : 
     216         102 :         if(!strncasecmp(client_data->recv_buffer, "noop", 4)){
     217           1 :                 logprintf(log, LOG_INFO, "Client noop\n");
     218           1 :                 client_send(log, client, "250 OK\r\n");
     219           1 :                 return 0;
     220             :         }
     221             : 
     222         101 :         if(!strncasecmp(client_data->recv_buffer, "rset", 4)){
     223          29 :                 logprintf(log, LOG_INFO, "Client reset\n");
     224          29 :                 mail_reset(&(client_data->current_mail));
     225          29 :                 client_send(log, client, "250 Reset OK\r\n");
     226          29 :                 return 0;
     227             :         }
     228             : 
     229             :         #ifndef CMAIL_NO_TLS
     230             :         //accept only when offered, reject when already negotiated
     231          72 :         if(!strncasecmp(client_data->recv_buffer, "starttls", 8)){
     232           6 :                 if(client->tls_mode!=TLS_NONE){
     233           1 :                         logprintf(log, LOG_WARNING, "Client with active TLS session tried to negotiate\n");
     234           1 :                         client_send(log, client, "503 Already in TLS session\r\n");
     235           1 :                         return 0;
     236             :                 }
     237             : 
     238           5 :                 if(client_data->listener->tls_mode!=TLS_NEGOTIATE){
     239           0 :                         logprintf(log, LOG_WARNING, "Client tried to negotiate TLS with non-negotiable listener\n");
     240           0 :                         client_send(log, client, "503 Not advertised\r\n");
     241             : 
     242             :                         //increase the failscore
     243           0 :                         return -1;
     244             :                 }
     245             : 
     246           5 :                 logprintf(log, LOG_INFO, "Client wants to negotiate TLS\n");
     247           5 :                 mail_reset(&(client_data->current_mail));
     248           5 :                 client_data->state=STATE_NEW;
     249             : 
     250           5 :                 client_send(log, client, "220 Go ahead\r\n");
     251             : 
     252           5 :                 client->tls_mode=TLS_NEGOTIATE;
     253             : 
     254             :                 //this is somewhat dodgy and should probably be replaced by a proper conditional
     255           5 :                 return tls_init_serverpeer(log, client, listener_data->tls_priorities, listener_data->tls_cert);
     256             :         }
     257             :         #endif
     258             : 
     259          66 :         if(!strncasecmp(client_data->recv_buffer, "auth ", 5)){
     260          17 :                 switch(listener_data->auth_offer){
     261             :                         case AUTH_NONE:
     262           3 :                                 logprintf(log, LOG_WARNING, "Client tried to auth on a non-auth listener\n");
     263           3 :                                 client_send(log, client, "503 Not advertised\r\n");
     264           3 :                                 return -1;
     265             :                         case AUTH_TLSONLY:
     266             :                                 #ifndef CMAIL_NO_TLS
     267          14 :                                 if(client->tls_mode != TLS_ONLY){
     268             :                                 #endif
     269           2 :                                         logprintf(log, LOG_WARNING, "Non-TLS client tried to auth on auth-tlsonly listener\n");
     270           2 :                                         client_send(log, client, "504 Encryption required\r\n"); //FIXME 538 might be better, but is marked obsolete
     271           2 :                                         return -1;
     272             :                                 #ifndef CMAIL_NO_TLS
     273             :                                 }
     274             :                                 #endif
     275             :                         case AUTH_ANY:
     276          12 :                                 if(client_data->sasl_user.authenticated){
     277           1 :                                         logprintf(log, LOG_WARNING, "Authenticated client tried to authenticate again\n");
     278           1 :                                         client_send(log, client, "503 Already authenticated\r\n");
     279           1 :                                         return -1;
     280             :                                 }
     281             :                                 //continue
     282          11 :                                 break;
     283             :                 }
     284             : 
     285          11 :                 client_data->state = STATE_AUTH;
     286          11 :                 return smtpstate_auth(log, client, database, path_pool);
     287             :         }
     288             : 
     289          49 :         if(!strncasecmp(client_data->recv_buffer, "xyzzy", 5)){
     290           1 :                 client_send(log, client, "250 Nothing happens\r\n");
     291           1 :                 logprintf(log, LOG_INFO, "Client performs incantation\n");
     292             :                 //Using this command for some debug output...
     293           1 :                 logprintf(log, LOG_DEBUG, "Client protocol: %s\n", client_data->current_mail.protocol);
     294           1 :                 logprintf(log, LOG_DEBUG, "Peer name %s, mail submitter %s, data_allocated %d\n", client_data->peer_name, client_data->current_mail.submitter, client_data->current_mail.data_allocated);
     295           1 :                 logprintf(log, LOG_DEBUG, "AUTH Status %s, Authentication: %s, Authorization: %s\n", client_data->sasl_context.method!=SASL_INVALID?"active":"inactive", client_data->sasl_user.authenticated ? client_data->sasl_user.authenticated:"none", client_data->sasl_user.authorized ? client_data->sasl_user.authorized:"none");
     296           1 :                 logprintf(log, LOG_DEBUG, "Originating router: %s (%s)\n", client_data->originating_route.router?client_data->originating_route.router:"none", client_data->originating_route.argument?client_data->originating_route.argument:"none");
     297             :                 #ifndef CMAIL_NO_TLS
     298           1 :                 logprintf(log, LOG_DEBUG, "TLS State: %s\n", tls_modestring(client->tls_mode));
     299             :                 #endif
     300           1 :                 logprintf(log, LOG_DEBUG, "Connection score: %d\n", client_data->connection_score);
     301           1 :                 return 0;
     302             :         }
     303             : 
     304          48 :         if(!strncasecmp(client_data->recv_buffer, "quit", 4)){
     305           3 :                 logprintf(log, LOG_INFO, "Client quit\n");
     306           3 :                 client_send(log, client, "221 OK Bye\r\n");
     307           3 :                 return client_close(client);
     308             :         }
     309             : 
     310             :         //this needs to be implemented as per RFC5321 3.3
     311          45 :         if(!strncasecmp(client_data->recv_buffer, "rcpt ", 5)){
     312           1 :                 logprintf(log, LOG_INFO, "Client tried to use RCPT in IDLE\n");
     313           1 :                 client_send(log, client, "503 Bad sequence of commands\r\n");
     314           1 :                 return -1;
     315             :         }
     316             : 
     317          44 :         if(!strncasecmp(client_data->recv_buffer, "mail from:", 10)){
     318          37 :                 if(listener_data->auth_require && !client_data->sasl_user.authenticated){
     319           0 :                         logprintf(log, LOG_WARNING, "Client tried mail command without authentication on strict auth listener\n");
     320           0 :                         client_send(log, client, "530 Authentication required\r\n");
     321           0 :                         return -1;
     322             :                 }
     323             : 
     324          37 :                 logprintf(log, LOG_INFO, "Client initiates mail transaction\n");
     325             :                 //extract reverse path and store it
     326          37 :                 if(path_parse(log, client_data->recv_buffer + 10, &(client_data->current_mail.reverse_path)) < 0){
     327           0 :                         client_send(log, client, "501 Path rejected\r\n");
     328           0 :                         return -1;
     329             :                 }
     330             : 
     331             :                 //resolve reverse path
     332          37 :                 switch(path_resolve(log, &(client_data->current_mail.reverse_path), database, client_data->sasl_user.authorized, true)){
     333             :                         case 0:
     334             :                                 //reverse path contains either store or null router.
     335             :                                 //check origination routing data for action to take
     336          37 :                                 if(client_data->sasl_user.authenticated && client_data->sasl_user.authorized){
     337          12 :                                         if(!client_data->originating_route.router || !strcmp(client_data->originating_route.router, "reject")){
     338           0 :                                                 client_send(log, client, "551 %s\r\n", client_data->originating_route.argument ? client_data->originating_route.argument:"User not allowed to use this path");
     339           0 :                                                 path_reset(&(client_data->current_mail.reverse_path));
     340           0 :                                                 return -1;
     341             :                                         }
     342          12 :                                         else if(!strcmp(client_data->originating_route.router, "any")){
     343             :                                                 //accept anything
     344             :                                         }
     345           0 :                                         else if(!strcmp(client_data->originating_route.router, "defined")){
     346           0 :                                                 if(!client_data->current_mail.reverse_path.route.router
     347           0 :                                                                 || !client_data->current_mail.reverse_path.route.argument
     348           0 :                                                                 || strcmp(client_data->current_mail.reverse_path.route.router, "store")
     349           0 :                                                                 || strcmp(client_data->current_mail.reverse_path.route.argument, client_data->sasl_user.authorized)){
     350             :                                                         //no valid store router pointing back at the user, fail the reverse path
     351           0 :                                                         client_send(log, client, "551 User not allowed to use this path\r\n");
     352           0 :                                                         path_reset(&(client_data->current_mail.reverse_path));
     353           0 :                                                         return -1;
     354             :                                                 }
     355             :                                                 else{
     356             :                                                         //the path resolves back to the originating user, accept it
     357             :                                                 }
     358             :                                         }
     359             :                                 }
     360             :                                 else{
     361             :                                         //filtering of local reverse paths for unauthenticated connections might take place here
     362             :                                 }
     363          37 :                                 break;
     364             :                         case 1:
     365             :                                 //inbound reject router (should not be able to happen anymore)
     366           0 :                                 logprintf(log, LOG_ERROR, "Inbound reject router applied to reverse path, please notify the developers!\n");
     367           0 :                                 break;
     368             :                         default:
     369             :                                 //resolution failed
     370           0 :                                 logprintf(log, LOG_INFO, "Failed to resolve reverse path\n");
     371           0 :                                 path_reset(&(client_data->current_mail.reverse_path));
     372           0 :                                 client_send(log, client, "451 Path rejected\r\n");
     373           0 :                                 return 0;
     374             :                 }
     375             : 
     376             :                 //TODO call plugins for spf, etc
     377             : 
     378          37 :                 client_send(log, client, "250 OK\r\n");
     379          37 :                 client_data->state = STATE_RECIPIENTS;
     380          37 :                 return 0;
     381             :         }
     382             : 
     383           7 :         if(!strncasecmp(client_data->recv_buffer, "vrfy ", 5)
     384           6 :                         || !strncasecmp(client_data->recv_buffer, "expn ", 5)){
     385           2 :                 logprintf(log, LOG_WARNING, "Client tried to verify / expand an address, unsupported\n");
     386           2 :                 client_send(log, client, "502 Not implemented\r\n");
     387           2 :                 return -1;
     388             :         }
     389             : 
     390           5 :         logprintf(log, LOG_INFO, "Command not recognized in state IDLE: %s\n", client_data->recv_buffer);
     391           5 :         client_send(log, client, "500 Unknown command\r\n");
     392           5 :         return -1;
     393             : }
     394             : 
     395          89 : int smtpstate_recipients(LOGGER log, CONNECTION* client, DATABASE* database, PATHPOOL* path_pool){
     396          89 :         CLIENT* client_data = (CLIENT*)client->aux_data;
     397          89 :         LISTENER* listener_data = (LISTENER*)client_data->listener->aux_data;
     398             :         unsigned i;
     399             :         MAILPATH* current_path;
     400             : 
     401          89 :         if(!strncasecmp(client_data->recv_buffer, "rcpt to:", 8)){
     402             :                 //get slot in forward_paths
     403          83 :                 for(i = 0; i < SMTP_MAX_RECIPIENTS; i++){
     404          83 :                         if(!client_data->current_mail.forward_paths[i]){
     405          48 :                                 break;
     406             :                         }
     407             :                 }
     408             : 
     409          48 :                 if(i == SMTP_MAX_RECIPIENTS){
     410             :                         //too many recipients, fail this one
     411           0 :                         logprintf(log, LOG_INFO, "Mail exceeded recipient limit\n");
     412           0 :                         client_send(log, client, "452 Too many recipients\r\n");
     413           0 :                         return -1;
     414             :                 }
     415             : 
     416             :                 //get path from pool
     417          48 :                 current_path = pathpool_get(log, path_pool);
     418          48 :                 if(!current_path){
     419           0 :                         logprintf(log, LOG_ERROR, "Failed to get path, failing recipient\n");
     420           0 :                         client_send(log, client, "452 Recipients pool maxed out\r\n");
     421             :                         //FIXME should state transition back to idle here?
     422           0 :                         return -1;
     423             :                 }
     424             : 
     425          48 :                 if(path_parse(log, client_data->recv_buffer + 8, current_path) < 0){
     426           3 :                         client_send(log, client, "501 Path rejected\r\n");
     427           3 :                         pathpool_return(current_path);
     428           3 :                         return -1;
     429             :                 }
     430             : 
     431             :                 //the last 2 parameters in this call _must_ be NULL/false to not trigger an invalid branch in this case
     432          45 :                 switch(path_resolve(log, current_path, database, NULL, false)){
     433             :                         case 0:
     434             :                                 //continue path handling
     435          40 :                                 break;
     436             :                         case 1:
     437             :                                 //reject by router decision
     438           5 :                                 client_send(log, client, "551 %s\r\n", current_path->route.argument ? current_path->route.argument:"User does not accept mail");
     439           5 :                                 pathpool_return(current_path);
     440           5 :                                 return -1;
     441             :                         default:
     442           0 :                                 client_send(log, client, "451 Path rejected\r\n");
     443           0 :                                 pathpool_return(current_path);
     444             :                                 //FIXME should state transition back to idle here?
     445             :                                 //client_data->state=STATE_IDLE;
     446             :                                 //mail_reset(&(client_data->current_mail));
     447           0 :                                 return 0;
     448             :                 }
     449             : 
     450          40 :                 if(!current_path->route.router){
     451             :                         //path not local, accept only if authenticated
     452           1 :                         if(!client_data->sasl_user.authenticated){
     453           1 :                                 client_send(log, client, "551 Unknown user\r\n");
     454           1 :                                 pathpool_return(current_path);
     455           1 :                                 return -1;
     456             :                         }
     457             :                 }
     458             : 
     459             :                 //FIXME address deduplication?
     460             :                 //TODO call plugins
     461             : 
     462          39 :                 client_data->current_mail.forward_paths[i] = current_path;
     463          39 :                 client_send(log, client, "250 Accepted\r\n");
     464             : 
     465             :                 //decrease the failscore
     466          39 :                 return 1;
     467             :         }
     468             : 
     469          41 :         if(!strncasecmp(client_data->recv_buffer, "quit", 4)){
     470           1 :                 logprintf(log, LOG_INFO, "Client quit\n");
     471           1 :                 client_send(log, client, "221 OK Bye\r\n");
     472           1 :                 return client_close(client);
     473             :         }
     474             : 
     475          40 :         if(!strncasecmp(client_data->recv_buffer, "noop", 4)){
     476           1 :                 logprintf(log, LOG_INFO, "Client noop\n");
     477           1 :                 client_send(log, client, "250 OK\r\n");
     478           1 :                 return 0;
     479             :         }
     480             : 
     481          39 :         if(!strncasecmp(client_data->recv_buffer, "rset", 4)){
     482           4 :                 client_data->state = STATE_IDLE;
     483           4 :                 mail_reset(&(client_data->current_mail));
     484           4 :                 logprintf(log, LOG_INFO, "Client reset\n");
     485           4 :                 client_send(log, client, "250 Reset OK\r\n");
     486           4 :                 return 0;
     487             :         }
     488             : 
     489          35 :         if(!strncasecmp(client_data->recv_buffer, "auth", 4)){
     490           1 :                 logprintf(log, LOG_INFO, "Client tried to use AUTH in RECIPIENTS\n");
     491           1 :                 client_send(log, client, "503 Bad sequence of commands\r\n");
     492           1 :                 return -1;
     493             :         }
     494             : 
     495          34 :         if(!strncasecmp(client_data->recv_buffer, "data", 4)){
     496             :                 //reject command if no recipients
     497          33 :                 if(!client_data->current_mail.forward_paths[0]){
     498           2 :                         logprintf(log, LOG_INFO, "Data without recipients\n");
     499           2 :                         client_send(log, client, "503 No valid recipients\r\n");
     500           2 :                         return -1;
     501             :                 }
     502          31 :                 client_data->state = STATE_DATA;
     503             : 
     504             :                 //write received: header
     505          31 :                 if(mail_recvheader(log, &(client_data->current_mail), listener_data->announce_domain, listener_data->suppress_submitter) < 0){
     506           0 :                         logprintf(log, LOG_WARNING, "Failed to write received header\n");
     507             :                 }
     508             : 
     509          31 :                 logprintf(log, LOG_INFO, "Client begins data transmission\n");
     510             : 
     511          31 :                 client_send(log, client, "354 Go ahead\r\n");
     512          31 :                 return 0;
     513             :         }
     514             : 
     515           1 :         logprintf(log, LOG_INFO, "Command not recognized in state RECIPIENTS: %s\n", client_data->recv_buffer);
     516           1 :         client_send(log, client, "500 Unknown command\r\n");
     517           1 :         return -1;
     518             : }
     519             : 
     520         102 : int smtpstate_data(LOGGER log, CONNECTION* client, DATABASE* database, PATHPOOL* path_pool){
     521         102 :         CLIENT* client_data = (CLIENT*)client->aux_data;
     522             : 
     523         102 :         if(client_data->recv_buffer[0] == '.'){
     524          31 :                 if(client_data->recv_buffer[1]){
     525           0 :                         logprintf(log, LOG_INFO, "Data line with leading dot, fixing\n");
     526             :                         //skip leading dot
     527             :                         //FIXME use return value (might indicate message too long)
     528           0 :                         if(mail_line(log, &(client_data->current_mail), client_data->recv_buffer + 1) < 0){
     529           0 :                                 logprintf(log, LOG_WARNING, "Failed to store mail data line\n");
     530             :                         }
     531           0 :                         return 0;
     532             :                 }
     533             :                 else{
     534             :                         //end of mail data signalled
     535             :                         //check if mail was too big
     536          31 :                         if(client_data->current_mail.data_max && client_data->current_mail.data_offset > client_data->current_mail.data_max){
     537           0 :                                 logprintf(log, LOG_WARNING, "End of mail, data section exceeded size limitation, rejecting\n");
     538           0 :                                 client_send(log, client, "552 Size limit exceeded\r\n");
     539             :                         }
     540             :                         //check for too many hops
     541          31 :                         else if(client_data->current_mail.hop_count > CMAIL_MAX_HOPS){
     542           0 :                                 logprintf(log, LOG_WARNING, "Mail exceeded maximum hop count of %d", CMAIL_MAX_HOPS);
     543           0 :                                 client_send(log, client, "554 Too many hops\r\n");
     544             :                         }
     545             :                         else{
     546          31 :                                 logprintf(log, LOG_INFO, "End of mail data, routing\n");
     547             : 
     548             :                                 //TODO call plugins here
     549          31 :                                 if(!client_data->sasl_user.authenticated){
     550          21 :                                         switch(mail_route(log, &(client_data->current_mail), database)){
     551             :                                                 case 250:
     552          18 :                                                         logprintf(log, LOG_INFO, "Incoming mail accepted from %s\n", client_data->peer_name);
     553          18 :                                                         client_send(log, client, "250 OK\r\n");
     554          18 :                                                         break;
     555             :                                                 case 400:
     556           0 :                                                         logprintf(log, LOG_INFO, "Temporary routing failure, deferring message\n");
     557           0 :                                                         client_send(log, client, "451 Temporary failure, please try again later. If the error persists, contact the administrator.\r\n");
     558           0 :                                                         break;
     559             :                                                 default:
     560           3 :                                                         logprintf(log, LOG_WARNING, "Mail not routed, rejecting\n");
     561           3 :                                                         client_send(log, client, "554 Rejected\r\n");
     562           3 :                                                         break;
     563             :                                         }
     564             :                                 }
     565             :                                 else{
     566          10 :                                         switch(mail_originate(log, client_data->sasl_user.authorized, &(client_data->current_mail), client_data->originating_route, database)){
     567             :                                                 case 250:
     568           7 :                                                         logprintf(log, LOG_INFO, "Originating mail accepted for user %s (auth %s) from %s\n", client_data->sasl_user.authorized, client_data->sasl_user.authenticated, client_data->peer_name);
     569           7 :                                                         client_send(log, client, "250 OK\r\n");
     570           7 :                                                         break;
     571             :                                                 case 400:
     572           0 :                                                         logprintf(log, LOG_INFO, "Temporary routing failure, deferring message\n");
     573           0 :                                                         client_send(log, client, "451 Temporary failure, please try again later. If the error persists, contact the administrator.\r\n");
     574           0 :                                                         break;
     575             :                                                 default:
     576           3 :                                                         logprintf(log, LOG_WARNING, "Originated mail could not be routed, rejecting\n");
     577           3 :                                                         client_send(log, client, "554 Rejected\r\n");
     578           3 :                                                         break;
     579             :                                         }
     580             :                                 }
     581             :                         }
     582          31 :                         mail_reset(&(client_data->current_mail));
     583          31 :                         client_data->state=STATE_IDLE;
     584             :                 }
     585             :         }
     586             :         else{
     587             :                 //detect end of header & count hops
     588          71 :                 if(client_data->current_mail.header_offset == 0){
     589             :                         //header end
     590          43 :                         if(client_data->recv_buffer[0] == 0){
     591           4 :                                 client_data->current_mail.header_offset = client_data->current_mail.data_offset;
     592           4 :                                 logprintf(log, LOG_DEBUG, "End of header detected at %d\n", client_data->current_mail.header_offset);
     593             :                         }
     594             :                         //count Received: headers
     595          39 :                         else if(!strncmp(client_data->recv_buffer, "Received: ", 10)){
     596           0 :                                 client_data->current_mail.hop_count++;
     597           0 :                                 logprintf(log, LOG_DEBUG, "Mail is now at %d hops\n", client_data->current_mail.hop_count);
     598             :                         }
     599             :                 }
     600             : 
     601             :                 //FIXME use return value (might indicate message too long)
     602          71 :                 if(mail_line(log, &(client_data->current_mail), client_data->recv_buffer) < 0){
     603           0 :                         logprintf(log, LOG_WARNING, "Failed to store mail data line\n");
     604             :                 }
     605          71 :                 return 0;
     606             :         }
     607             : 
     608          31 :         return 0;
     609             : }

Generated by: LCOV version 1.11