LCOV - code coverage report
Current view: top level - cmail-popd - client.c (source / functions) Hit Total Coverage
Test: popd.info Lines: 80 111 72.1 %
Date: 2015-11-25 19:05:59 Functions: 5 5 100.0 %

          Line data    Source code
       1          83 : int client_line(LOGGER log, CONNECTION* client, DATABASE* database){
       2          83 :         CLIENT* client_data = (CLIENT*)client->aux_data;
       3             : 
       4          83 :         logprintf(log, LOG_ALL_IO, ">> %s\n", client_data->recv_buffer);
       5             : 
       6          83 :         switch(client_data->state){
       7             :                 case STATE_AUTH:
       8          71 :                         return state_authorization(log, client, database);
       9             :                 case STATE_TRANSACTION:
      10          12 :                         return state_transaction(log, client, database);
      11             :                 case STATE_UPDATE:
      12           0 :                         return state_update(log, client, database);
      13             :         }
      14             : 
      15           0 :         return 0;
      16             : }
      17             : 
      18          12 : int client_accept(LOGGER log, CONNECTION* listener, CONNPOOL* clients){
      19          12 :         int client_slot = -1, flags, status;
      20          24 :         CLIENT empty_data = {
      21             :                 .listener = listener,
      22             :                 .recv_offset = 0,
      23          12 :                 .last_action = time(NULL),
      24             :                 .connection_score = 0,
      25             :                 .state = STATE_AUTH,
      26             :                 .maildrop = {
      27             :                         .count = 0,
      28             :                         .mails = NULL,
      29             :                         .user_conn = NULL,
      30             :                         .list_user = NULL,
      31             :                         .fetch_user = NULL,
      32             :                         .mark_deletion = NULL,
      33             :                         .unmark_deletions = NULL,
      34             :                         .delete_user = NULL
      35             :                 },
      36             :                 .auth = {
      37             :                         .method = AUTH_USER,
      38             :                         .auth_ok = false,
      39             :                         .ctx = {
      40             :                                 .method = SASL_INVALID
      41             :                                 //rest handled by sasl_begin
      42             :                         },
      43             :                         .user = {
      44             :                                 .authenticated  = NULL,
      45             :                                 .authorized = NULL
      46             :                         }
      47             :                 }
      48             :         };
      49             :         CLIENT* actual_data;
      50          12 :         LISTENER* listener_data = (LISTENER*)listener->aux_data;
      51             : 
      52          12 :         if(connpool_active(*clients) >= CMAIL_MAX_CONCURRENT_CLIENTS){
      53           0 :                 logprintf(log, LOG_INFO, "Not accepting new client, limit reached\n");
      54           0 :                 return 1;
      55             :         }
      56             : 
      57          12 :         client_slot = connpool_add(clients, accept(listener->fd, NULL, NULL));
      58             : 
      59          12 :         if(client_slot < 0){
      60           0 :                 logprintf(log, LOG_ERROR, "Failed to pool client socket\n");
      61           0 :                 return -1;
      62             :         }
      63             : 
      64             :         //set socket nonblocking
      65          12 :         flags = fcntl(clients->conns[client_slot].fd, F_GETFL, 0);
      66          12 :         if(flags < 0){
      67           0 :                 flags=0;
      68             :         }
      69          12 :         status = fcntl(clients->conns[client_slot].fd, F_SETFL, flags | O_NONBLOCK);
      70          12 :         if(status < 0){
      71           0 :                 logprintf(log, LOG_ERROR, "Failed to make client socket nonblocking: %s\n", strerror(errno));
      72             :         }
      73             : 
      74          12 :         if(!(clients->conns[client_slot].aux_data)){
      75           1 :                 clients->conns[client_slot].aux_data = malloc(sizeof(CLIENT));
      76           1 :                 if(!clients->conns[client_slot].aux_data){
      77           0 :                         logprintf(log, LOG_ERROR, "Failed to allocate client data set\n");
      78           0 :                         return -1;
      79             :                 }
      80             :         }
      81             :         else{
      82             :                 //preserve old data
      83             :                 //none
      84             :         }
      85             : 
      86             :         //initialise / reset client data structure
      87          12 :         actual_data = (CLIENT*)clients->conns[client_slot].aux_data;
      88          12 :         *actual_data = empty_data;
      89             : 
      90             :         #ifndef CMAIL_NO_TLS
      91             :         //if on tlsonly port, immediately wait for negotiation
      92          12 :         if(listener->tls_mode == TLS_ONLY){
      93           2 :                 logprintf(log, LOG_INFO, "Listen socket is TLSONLY, waiting for negotiation...\n");
      94           2 :                 clients->conns[client_slot].tls_mode = TLS_NEGOTIATE;
      95           2 :                 return tls_init_serverpeer(log, &(clients->conns[client_slot]), listener_data->tls_priorities, listener_data->tls_cert);
      96             :         }
      97             :         #endif
      98             : 
      99          10 :         client_send(log, &(clients->conns[client_slot]), "+OK %s POP3 ready\r\n", listener_data->announce_domain);
     100          10 :         return 0;
     101             : }
     102             : 
     103           1 : bool client_timeout(LOGGER log, CONNECTION* client){
     104           1 :         CLIENT* client_data = (CLIENT*)client->aux_data;
     105           1 :         int delta = time(NULL) - client_data->last_action;
     106             : 
     107           1 :         if(delta < 0){
     108           0 :                 logprintf(log, LOG_ERROR, "Time reported an error or skipped ahead: %s\n", strerror(errno));
     109           0 :                 return false;
     110             :         }
     111             : 
     112           1 :         logprintf(log, LOG_DEBUG, "Client has activity delta %d seconds\n", delta);
     113           1 :         return delta > POP_SERVER_TIMEOUT;
     114             : }
     115             : 
     116          12 : int client_close(LOGGER log, CONNECTION* client, DATABASE* database){
     117          12 :         CLIENT* client_data = (CLIENT*)client->aux_data;
     118             : 
     119             :         #ifndef CMAIL_NO_TLS
     120             :         //shut down the tls session
     121          12 :         if(client->tls_mode != TLS_NONE){
     122          10 :                 gnutls_bye(client->tls_session, GNUTLS_SHUT_RDWR);
     123          10 :                 gnutls_deinit(client->tls_session);
     124          10 :                 client->tls_mode = TLS_NONE;
     125             :         }
     126             :         #endif
     127             : 
     128             :         //close the socket
     129          12 :         close(client->fd);
     130             : 
     131             :         //reset client data
     132          12 :         if(client_data->auth.auth_ok && client_data->auth.user.authenticated){
     133           7 :                 maildrop_release(log, database, &(client_data->maildrop), client_data->auth.user.authorized);
     134             :         }
     135          12 :         auth_reset(&(client_data->auth));
     136             : 
     137             :         //return the conpool slot
     138          12 :         client->fd = -1;
     139             : 
     140          12 :         return 0;
     141             : }
     142             : 
     143         107 : int client_process(LOGGER log, CONNECTION* client, DATABASE* database){
     144         107 :         CLIENT* client_data = (CLIENT*)client->aux_data;
     145         107 :         LISTENER* listener_data = (LISTENER*)client_data->listener->aux_data;
     146             :         ssize_t left, bytes, line_length;
     147             : 
     148             :         #ifndef CMAIL_NO_TLS
     149             :         do{
     150             :         #endif
     151         107 :         left = sizeof(client_data->recv_buffer) - client_data->recv_offset;
     152             : 
     153         107 :         if(left < 2){
     154             :                 //unterminated line
     155             :                 //FIXME this might be kind of a harsh response
     156           0 :                 logprintf(log, LOG_WARNING, "Line too long, closing client connection\n");
     157           0 :                 client_send(log, client, "-ERR Line too long\r\n");
     158           0 :                 client_close(log, client, database);
     159           0 :                 return 0;
     160             :         }
     161             : 
     162         107 :         bytes = network_read(log, client, client_data->recv_buffer+client_data->recv_offset, left);
     163             : 
     164             :         //failed to read from socket
     165         107 :         if(bytes < 0){
     166             :                 #ifndef CMAIL_NO_TLS
     167          12 :                 switch(client->tls_mode){
     168             :                         case TLS_NONE:
     169             :                 #endif
     170           0 :                 switch(errno){
     171             :                         case EAGAIN:
     172           0 :                                 logprintf(log, LOG_WARNING, "Read signaled, but blocked\n");
     173           0 :                                 return 0;
     174             :                         default:
     175           0 :                                 logprintf(log, LOG_ERROR, "Failed to read from client: %s\n", strerror(errno));
     176           0 :                                 client_close(log, client, database);
     177           0 :                                 return -1;
     178             :                 }
     179             :                 #ifndef CMAIL_NO_TLS
     180             :                         break;
     181             :                         case TLS_NEGOTIATE:
     182             :                                 //errors during TLS negotiation
     183          10 :                                 if(bytes == -2){
     184           0 :                                         client_close(log, client, database);
     185             :                                 }
     186          10 :                                 return 0;
     187             :                         case TLS_ONLY:
     188           2 :                                 switch(bytes){
     189             :                                         case GNUTLS_E_INTERRUPTED:
     190             :                                         case GNUTLS_E_AGAIN:
     191           0 :                                                 logprintf(log, LOG_WARNING, "TLS read signaled, but blocked\n");
     192           0 :                                                 return 0;
     193             :                                         default:
     194           2 :                                                 logprintf(log, LOG_ERROR, "GnuTLS reported an error while reading: %s\n", gnutls_strerror(bytes));
     195           2 :                                                 client_close(log, client, database);
     196           2 :                                                 return -1;
     197             :                                 }
     198             :                 }
     199             :                 #endif
     200             :         }
     201             : 
     202             :         //client disconnect / handshake success
     203          95 :         else if(bytes == 0){
     204             :                 #ifndef CMAIL_NO_TLS
     205          12 :                 switch(client->tls_mode){
     206             :                         case TLS_NEGOTIATE:
     207             :                                 //tls handshake ok
     208          10 :                                 client->tls_mode = TLS_ONLY;
     209          10 :                                 if(client_data->listener->tls_mode == TLS_ONLY){
     210             :                                         //send greeting if listener is tlsonly
     211           2 :                                         client_send(log, client, "+OK %s POP3 ready\r\n", listener_data->announce_domain);
     212             :                                 }
     213          10 :                                 break;
     214             :                         default:
     215             :                 #endif
     216           2 :                 logprintf(log, LOG_INFO, "Client has disconnected\n");
     217           2 :                 client_close(log, client, database);
     218             :                 #ifndef CMAIL_NO_TLS
     219             :                 }
     220             :                 #endif
     221          12 :                 return 0;
     222             :         }
     223             : 
     224          83 :         logprintf(log, LOG_DEBUG, "Received %d bytes of data, recv_offset is %d\n", bytes, client_data->recv_offset);
     225             : 
     226             :         do{
     227         166 :                 line_length = common_next_line(log, client_data->recv_buffer, &(client_data->recv_offset), &bytes);
     228         166 :                 if(line_length >= 0){
     229          83 :                         if(line_length >= POP_MAX_LINE_LENGTH-2){
     230           0 :                                 logprintf(log, LOG_WARNING, "Line too long, ignoring\n");
     231           0 :                                 client_send(log, client, "-ERR Line too long\r\n");
     232             :                                 //client_line(log, client, database);
     233             :                                 //FIXME might handle this more sensibly
     234             :                         }
     235             :                         else{
     236             :                                 //update last action timestamp
     237          83 :                                 client_data->last_action = time(NULL);
     238             : 
     239          83 :                                 client_data->connection_score += client_line(log, client, database);
     240             : 
     241             :                                 //kick the client after too many failed commands
     242          83 :                                 if(client_data->connection_score < CMAIL_FAILSCORE_LIMIT){
     243           0 :                                         logprintf(log, LOG_WARNING, "Disconnecting client because of bad connection score\n");
     244           0 :                                         client_send(log, client, "-ERR Too many failed commands\r\n");
     245           0 :                                         client_close(log, client, database);
     246           0 :                                         return 0;
     247             :                                 }
     248             :                         }
     249             :                 }
     250             :         }
     251         166 :         while(line_length >= 0);
     252             : 
     253             :         #ifndef CMAIL_NO_TLS
     254             :         }
     255          83 :         while(client->tls_mode == TLS_ONLY && gnutls_record_check_pending(client->tls_session));
     256             :         #endif
     257             : 
     258          83 :         return 0;
     259             : }

Generated by: LCOV version 1.11