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 : }
|