1 /*
2 * ImapProxy - a caching IMAP proxy daemon
3 * Copyright (C) 2002 Steven Van Acker
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 *
19 */
20
21
22 #include <stdio.h>
23 #include <sys/types.h>
24 #include <sys/socket.h>
25 #include <sys/ioctl.h>
26 #include <net/if.h>
27 #include <netinet/in.h>
28 #include <sys/time.h>
29 #include <sys/types.h>
30 #include <sys/wait.h>
31 #include <unistd.h>
32 #include <string.h>
33 #include <strings.h>
34 #include <stdlib.h>
35 #include <netdb.h>
36 #include <unistd.h>
37 #include <fcntl.h>
38 #include <errno.h>
39 #include <arpa/inet.h>
40 #include <stdarg.h>
41 #include <ctype.h>
42 #include <time.h>
43
44 #include <output.h>
45 #include <network.h>
46 #include <base64.h>
47 #include <database.h>
48 #include <splitstring.h>
49 #include <configfile.h>
50 #include <processing_parent.h>
51 #include <processing_child.h>
52 #include <misc.h>
53 #include <defines.h>
54 #include <parsed_message.h>
55
56 extern int Globaltime;
57
58 /* this function checks if the server allows authenticate command.
59 * it checks the given split string for AUTH=LOGIN
60 */
parent_can_use_authenticate_login(Parsed_message * pm)61 int parent_can_use_authenticate_login(Parsed_message *pm)
62 {
63 int i,n;
64
65 return 0;
66
67 n = parsed_message_count_args(pm);
68
69 /* start at 2, because the string starts with * and CAPABILITY, so we can skip those
70 */
71 for(i = 2;i < n; i++)
72 {
73 if(!strcasecmp(parsed_message_get_arg(pm,i),"AUTH=LOGIN"))
74 {
75 debug("parent_can_use_authenticate_login(): AUTHENTICATE LOGIN is allowed !! using it...\n");
76 return 1;
77 }
78 }
79
80 debug("parent_can_use_authenticate_login(): AUTHENTICATE LOGIN is not allowed !! Falling back on LOGIN instead.\n");
81 return 0;
82 }
83
84 /* this function sets up a connection to the server
85 * and sets appropriate flags
86 */
parent_init_server_connection(DB_record * p)87 int parent_init_server_connection(DB_record *p)
88 {
89 struct sockaddr_in remote,local;
90 int size = 0;
91
92 if((p->serversocket = nonblock_socket()) < 0)
93 {
94 debug("parent_init_server_connection(): Couldn't not open a non-blocking socket for serverconnection !\n");
95 return -1;
96 }
97
98 bzero(&remote,sizeof(remote));
99 remote.sin_family = PF_INET;
100 remote.sin_port = htons(config_remote_port);
101 remote.sin_addr.s_addr = config_remote_address;
102
103 if(connect(p->serversocket,(const struct sockaddr *)&remote,sizeof(remote)))
104 {
105 if(errno != EINPROGRESS)
106 {
107 debug("parent_init_server_connection(): Couldn't connect to remote (%m)\n");
108 return -1;
109 }
110 }
111
112 size = sizeof(local);
113
114 if ((getsockname(p->serversocket,(struct sockaddr *) &local, &size ) < 0))
115 {
116 debug("parent_init_server_connection(): getsockname error ! (%s)\n",strerror(errno));
117 return -1;
118 }
119
120 p->proxylocalport = ntohs(local.sin_port);
121
122 db_set_server_connection_state(p,DB_STATE_SERVER_CONNECTION_STARTED);
123 return 0;
124 }
125
126 /* fork record and set flags */
parent_fork_record(DB_record * p,int mainfd)127 int parent_fork_record(DB_record *p,int mainfd)
128 {
129 DB_record *x = NULL,*y = NULL;
130
131 pid_t pid;
132
133 if(pipe(p->ipcfd))
134 {
135 debug("parent_conversate(): Couldn't create ipc pipe %m\n");
136 return -1;
137 }
138
139 pid = fork();
140
141 if(pid < 0)
142 {
143 debug("parent_conversate(): Couldn't fork ! (%m)\n");
144 return -1;
145 }
146 else if(pid == 0)
147 {
148 /* child process */
149
150 /* we don't need to listening port */
151 close(mainfd);
152
153 /* we don't need the other records either */
154
155 x = DB_start;
156
157 while(x)
158 {
159 y = x->next;
160
161 if(x != p)
162 {
163 /* remove all except ourselves */
164 db_del(x);
165 }
166
167 x = y;
168 }
169
170 db_set_record_state(p,DB_STATE_RECORD_FORKED_CHILD);
171
172 forkedchild = p;
173 /* reset the loop */
174 return 0;
175 }
176 else /* pid > 0 */
177 {
178 /* parent process */
179 db_set_record_state(p,DB_STATE_RECORD_ACTIVE);
180 p->pid = pid;
181 debug("Forked a child %d\n",pid);
182
183 /* set nonblocking */
184 if(fcntl(p->ipcfd[0],F_SETFL,O_NONBLOCK)<0)
185 {
186 debug("parent_conversate(): Couldn't set O_NONBLOCK (%m)\n");
187 return -1;
188 }
189
190 forkedchild = NULL;
191
192 /* we don't need the client anymore */
193 close(p->clientsocket);
194 p->clientsocket = -1;
195
196 return pid;
197 }
198
199 return -1;
200 }
201
202 /* return 0 on success
203 */
parent_locate_and_activate_record(DB_record * p)204 int parent_locate_and_activate_record(DB_record *p)
205 {
206 DB_record *r = NULL;
207
208 if(!(r = db_find_record(p,p->username,p->password)))
209 {
210 debug("parent_locate_and_activate_record(): No free record found for \"%s\"\n",p->username);
211 return -1;
212 }
213
214 /* we found an inactive record for this user.
215 *
216 * instead of reactivating it, we will copy the necessary data to the temporary record
217 * and remove the inactive one.
218 */
219
220 debug("parent_locate_and_activate_record(): Free record found for \"%s\". Reactivating ...\n",p->username);
221
222 p->serversocket = r->serversocket;
223 r->serversocket = -1;
224
225 /* remember the local port */
226 p->proxylocalport = r->proxylocalport;
227
228 p->reuse = r->reuse + 1;
229
230 /* we are ready to fork now */
231 db_set_record_state(p,DB_STATE_RECORD_READY_TO_FORK);
232
233 /* the server is connected */
234 db_set_server_connection_state(p,DB_STATE_SERVER_CONNECTED);
235
236 /* the server has authenticated us */
237 socket_printf(p->clientsocket,"%s OK Reactivating record\r\n",p->clientauthtag);
238 log(LOG_NOTICE,"%s Reactivating record for user \"%s\" (%d).\n",db_get_record_info(p),p->username,p->reuse);
239
240 db_set_server_state(p,DB_STATE_SERVER_SENT_LOGIN_OK);
241
242 /* the client is authenticated */
243 db_set_client_state(p,DB_STATE_CLIENT_AUTHENTICATED);
244
245 /* the proxy passed the LOGIN OK message to the client */
246 db_set_proxy_state(p,DB_STATE_PROXY_SENT_LOGIN_OK_TO_CLIENT);
247
248 db_set_misc_state(p,DB_STATE_MISC_NONE);
249
250 /* remove the old record though */
251 db_set_misc_state(r,DB_STATE_MISC_REMOVE_RECORD);
252
253
254
255 return 0;
256 }
257
258 /*
259 * This function handles a new incoming connection (connecting to the proxy)
260 * It should :
261 * check if the connection is allowed and close it if necessary
262 * create a new database record and initialise it.
263 */
parent_handle_new_connection(int fd)264 int parent_handle_new_connection(int fd)
265 {
266 int newfd;
267 DB_record rec;
268 struct sockaddr_in addr;
269 int addrlen = sizeof(struct sockaddr);
270
271 /* new connection arrived, add to database */
272 newfd = accept(fd,(struct sockaddr *)&addr,&addrlen);
273
274 if(newfd < 0)
275 {
276 debug("parent_handle_new_connection(): accept returned error: %m\n");
277 return -1;
278 }
279
280 log(LOG_NOTICE,"Connection received from %s:%d.\n",inet_ntoa(addr.sin_addr),ntohs(addr.sin_port));
281
282 if(check_if_blocked(addr.sin_addr.s_addr))
283 {
284 /* ip is blocked ! */
285 debug("[PARENT CLIENT %s:%d REFUSED]\n",inet_ntoa(addr.sin_addr),ntohs(addr.sin_port));
286 log(LOG_NOTICE,"Connection refused from %s:%d.\n",inet_ntoa(addr.sin_addr),ntohs(addr.sin_port));
287 socket_printf(newfd,"* BYE Access to proxy denied.\r\n");
288 close(newfd);
289 return 0;
290 }
291
292
293 db_init_record(&rec);
294 rec.clientsocket = newfd;
295
296 rec.clientlocaladdress.s_addr = addr.sin_addr.s_addr;
297 rec.clientlocalport = ntohs(addr.sin_port);
298
299 /* record will be temporary untill enough data is gathered to authenticate (or reject) it
300 */
301 db_set_record_state(&rec,DB_STATE_RECORD_TEMPORARY);
302
303 socket_printf(newfd,"* OK IMAP4rev1 "PROGNAME" "VERSION" Welcome to my dominion.\r\n");
304 db_set_proxy_state(&rec,DB_STATE_PROXY_SENT_GREETING_TO_CLIENT);
305
306 db_add(&rec);
307
308 debug("[PARENT CLIENT CONNECT] <%d>\n",rec.clientsocket);
309
310 return 0;
311 }
312
313
314 /*
315 * This function handles a closed connection from the client side.
316 * Whatever the current command to the server is, don't send it
317 */
parent_handle_client_connection_closed(DB_record * p)318 int parent_handle_client_connection_closed(DB_record *p)
319 {
320 debug("[PARENT CLIENT CLOSED] <%d>\n",p->clientsocket);
321
322 /* if a client is connected, we are not in DB_STATE_RECORD_INACTIVE mode
323 * DB_STATE_RECORD_ACTIVE is not possible (otherwise we would be forked)
324 * so, there is no server to send anything to.
325 */
326 db_set_misc_state(p,DB_STATE_MISC_REMOVE_RECORD);
327 return 0;
328 }
329
330 /* This function handles errors in the client connection.
331 */
332
parent_handle_client_connection_error(DB_record * p)333 int parent_handle_client_connection_error(DB_record *p)
334 {
335 debug("parent_handle_client_connection_error(): read() error on socket %d (%m)\n",p->clientsocket);
336
337 db_set_misc_state(p,DB_STATE_MISC_REMOVE_RECORD);
338 return 0;
339 }
340
341 #define INDEX_FOR_SERVER 0
342 #define INDEX_FOR_CLIENT 1
343
344 /* process message coming from the client */
parent_process_client_message(DB_record * p)345 int parent_process_client_message(DB_record *p)
346 {
347 struct message_t *currmessage = NULL;
348 int i = 0;
349 int argcount = 0;
350 char *tmp = NULL;
351 Parsed_message pm;
352
353 currmessage = &(p->clientmessage);
354
355 if(!currmessage->count || !db_get_client_message(p,0))
356 {
357 debug("empty message ?!?! how does this happen ??\n");
358 return 0;
359 }
360
361 if(db_get_client_state(p) == DB_STATE_CLIENT_AUTHENTICATED)
362 {
363 debug("parent_process_client_message(): We should not be in this state !!! Authenticated clients should fork.\n");
364 }
365
366 /* output some things for debug */
367
368 if(config_enable_debug)
369 {
370 for(i = 0;i < currmessage->count; i++)
371 {
372 if((tmp = db_get_client_message(p,i)))
373 {
374 if(!i)
375 debug("[PC:] %.80s%s",tmp,strlen(tmp)>80?"...\n":"");
376 else
377 debug("[PC:] ...\n");
378 }
379 }
380 }
381
382 /* parse the message */
383 init_parsed_message(&pm);
384 make_parsed_message(currmessage,&pm);
385
386 /* some debug */
387 print_parsed_message(&pm);
388
389 /* continuation of the authenicate command
390 * if user sends "*", then he/she wants to abort !
391 */
392 if(db_get_client_state(p) == DB_STATE_CLIENT_SENT_AUTHENTICATE_COMMAND && db_get_proxy_state(p) == DB_STATE_PROXY_SENT_USERNAME_REPLY_TO_CLIENT)
393 {
394 char username[DB_STRING_LENGTH+1];
395
396 tmp = parsed_message_get_idtag(&pm);
397
398 if(tmp)
399 {
400 if(!strcmp(tmp,"*"))
401 {
402 log(LOG_NOTICE,"%s User aborted login.\n",db_get_record_info(p));
403 socket_printf(p->clientsocket,"%s BAD AUTHENTICATE LOGIN cancelled\r\n",p->clientauthtag);
404 db_set_proxy_state(p,DB_STATE_PROXY_SENT_GREETING_TO_CLIENT);
405 db_set_client_state(p,DB_STATE_CLIENT_NOT_AUTHENTICATED);
406 free(p->clientauthtag);
407 p->clientauthtag = NULL;
408 goto c_real_end_process;
409 }
410 else
411 {
412 base64decode(username,tmp,strlen(tmp));
413 strncpy(p->username,username,DB_STRING_LENGTH);
414 }
415 }
416 else
417 {
418 strcpy(p->username,"");
419 }
420
421 db_set_client_state(p, DB_STATE_CLIENT_SENT_USERNAME);
422 debug("[PARENT CLIENT PROCESS] Client sent username \"%s\"\n",tmp?username:"");
423 log(LOG_NOTICE,"%s Authentication attempt for user \"%s\".\n",db_get_record_info(p),tmp?username:"");
424
425 /* send "Password" in base64 encoding */
426 socket_printf(p->clientsocket,"+ UGFzc3dvcmQA\r\n");
427 db_set_proxy_state(p,DB_STATE_PROXY_SENT_PASSWORD_REPLY_TO_CLIENT);
428
429 goto c_real_end_process;
430 }
431 else if(db_get_client_state(p) == DB_STATE_CLIENT_SENT_USERNAME && db_get_proxy_state(p) == DB_STATE_PROXY_SENT_PASSWORD_REPLY_TO_CLIENT)
432 {
433 char password[DB_STRING_LENGTH+1];
434
435 tmp = parsed_message_get_idtag(&pm);
436
437 if(tmp)
438 {
439 if(!strcmp(tmp,"*"))
440 {
441 log(LOG_NOTICE,"%s User \"%s\" aborted login.\n",db_get_record_info(p),p->username);
442 socket_printf(p->clientsocket,"%s BAD AUTHENTICATE LOGIN cancelled\r\n",p->clientauthtag);
443 db_set_proxy_state(p,DB_STATE_PROXY_SENT_GREETING_TO_CLIENT);
444 db_set_client_state(p,DB_STATE_CLIENT_NOT_AUTHENTICATED);
445 free(p->clientauthtag);
446 p->clientauthtag = NULL;
447 strcpy(p->username,"");
448 goto c_real_end_process;
449 }
450 else
451 {
452 base64decode(password,tmp,strlen(tmp));
453 strncpy(p->password,password,DB_STRING_LENGTH);
454 }
455 }
456 else
457 {
458 strcpy(p->password,"");
459 }
460
461
462 db_set_client_state(p, DB_STATE_CLIENT_SENT_PASSWORD);
463 debug("[PARENT CLIENT PROCESS] Client sent password\n");
464
465 /* check if we have a free slot, otherwise connect to server */
466 if(parent_locate_and_activate_record(p) == 0)
467 goto c_real_end_process;
468
469 if(parent_init_server_connection(p))
470 {
471 log(LOG_CRIT,"%s Could not initiate connection to server ?\n",db_get_record_info(p));
472 goto c_send_bye;
473 }
474 else
475 goto c_real_end_process;
476 }
477
478 /* we need at least 1 string to work with (ID tag) */
479 if((argcount = parsed_message_count_args(&pm)) < 1)
480 {
481 socket_printf(p->clientsocket,"* BAD Null command\r\n");
482 goto c_real_end_process;
483 }
484
485
486 if(argcount < 2)
487 {
488 socket_printf(p->clientsocket,"%s BAD Missing command\r\n",parsed_message_get_idtag(&pm));
489 goto c_real_end_process;
490 }
491
492 tmp = parsed_message_get_command(&pm);
493
494 if(db_get_proxy_state(p) == DB_STATE_PROXY_SENT_GREETING_TO_CLIENT && db_get_client_state(p) == DB_STATE_CLIENT_NOT_AUTHENTICATED && !strcasecmp(tmp,"AUTHENTICATE"))
495 {
496 if(argcount != 3)
497 {
498 debug("parent_process_client_message(): AUTHENTICATE attempted with invalid amount of arguments (argcount = %d).\n",argcount);
499 socket_printf(p->clientsocket,"%s BAD Missing or invalid argument to AUTHENTICATE\r\n",parsed_message_get_idtag(&pm));
500 goto c_real_end_process;
501 }
502
503 tmp = parsed_message_get_arg(&pm,2);
504 if(!strcasecmp(tmp,"LOGIN"))
505 {
506 debug("[PARENT CLIENT PROCESS] Authenticated started (AUTHENTICATE LOGIN)\n");
507 db_set_client_state(p, DB_STATE_CLIENT_SENT_AUTHENTICATE_COMMAND);
508
509 if(p->clientauthtag)
510 {
511 debug("parent_process_client_message(): Previous tag message in place (%s)!\n",p->clientauthtag);
512 }
513
514 if(!(p->clientauthtag = strdup(parsed_message_get_idtag(&pm))))
515 {
516 debug("parent_process_client_message(): Out of memory\n");
517 log(LOG_CRIT,"%s Out of memory\n",db_get_record_info(p));
518 goto c_send_bye;
519 }
520
521 /* send User Name in base64 */
522 socket_printf(p->clientsocket,"+ VXNlciBOYW1lAA==\r\n");
523 db_set_proxy_state(p,DB_STATE_PROXY_SENT_USERNAME_REPLY_TO_CLIENT);
524
525 goto c_real_end_process;
526 }
527 else
528 {
529 debug("parent_process_client_message(): Client sent unsupported authentication method (AUTHENTICATE %s)\n",tmp);
530 socket_printf(p->clientsocket,"%s NO AUTHENTICATE %s failed\r\n",parsed_message_get_idtag(&pm),parsed_message_get_arg(&pm,2));
531
532 goto c_real_end_process;
533 }
534 }
535 else if(db_get_proxy_state(p) == DB_STATE_PROXY_SENT_GREETING_TO_CLIENT && db_get_client_state(p) == DB_STATE_CLIENT_NOT_AUTHENTICATED && !strcasecmp(tmp,"LOGIN"))
536 {
537 if(argcount < 4)
538 {
539 debug("parent_process_client_message(): LOGIN attempted with invalid amount of arguments (argcount = %d).\n",argcount);
540 socket_printf(p->clientsocket,"%s BAD Missing or invalid argument to LOGIN\r\n",parsed_message_get_idtag(&pm));
541 goto c_real_end_process;
542 }
543
544 strncpy(p->username,parsed_message_get_arg(&pm,2),DB_STRING_LENGTH);
545 strncpy(p->password,parsed_message_get_arg(&pm,3),DB_STRING_LENGTH);
546
547 debug("[PARENT CLIENT PROCESS] LOGIN [%s] [*****]\n",p->username);
548 log(LOG_NOTICE,"%s Authentication attempt for user \"%s\".\n",db_get_record_info(p),p->username);
549
550 if(p->clientauthtag)
551 {
552 debug("parent_process_client_message(): Previous tag message in place (%s)!\n",p->clientauthtag);
553 free(p->clientauthtag);
554 p->clientauthtag = NULL;
555 }
556
557 if(!(p->clientauthtag = strdup(parsed_message_get_idtag(&pm))))
558 {
559 debug("parent_process_client_message(): Out of memory\n");
560 goto c_real_end_process;
561 }
562
563 db_set_client_state(p, DB_STATE_CLIENT_SENT_PASSWORD);
564
565 /* if we find an inactive connection, activate it
566 * otherwise, open a new one.
567 */
568 if(parent_locate_and_activate_record(p) == 0)
569 goto c_real_end_process;
570
571 if(parent_init_server_connection(p))
572 {
573 log(LOG_CRIT,"%s Could not initiate connection to server ?\n",db_get_record_info(p));
574 goto c_send_bye;
575 }
576 else
577 goto c_real_end_process;
578
579 }
580 else if(!strcasecmp(tmp,"LOGOUT"))
581 {
582 if(argcount != 2)
583 {
584 debug("parent_process_client_message(): LOGOUT attempted with invalid amount of arguments (argcount = %d).\n",argcount);
585 socket_printf(p->clientsocket,"%s BAD\r\n",parsed_message_get_idtag(&pm));
586 goto c_real_end_process;
587 }
588
589 debug("[PARENT CLIENT PROCESS] LOGOUT received\n");
590
591 /* print a nice message for the client to stare at, and close the connection */
592 socket_printf(p->clientsocket,"* BYE " PROGNAME " " VERSION " closing connection.\r\n");
593 socket_printf(p->clientsocket,"%s OK LOGOUT completed\r\n",parsed_message_get_idtag(&pm));
594
595 db_set_misc_state(p, DB_STATE_MISC_REMOVE_RECORD);
596
597 goto c_real_end_process;
598 }
599 else if(!strcasecmp(tmp,"NOOP"))
600 {
601 socket_printf(p->clientsocket,"%s OK NOOP completed\r\n",parsed_message_get_idtag(&pm));
602 goto c_real_end_process;
603 }
604 else if(!strcasecmp(tmp,"CAPABILITY"))
605 {
606 socket_printf(p->clientsocket,"* CAPABILITY IMAP4REV1 AUTH=LOGIN\r\n");
607 socket_printf(p->clientsocket,"%s OK CAPABILITY completed\r\n",parsed_message_get_idtag(&pm));
608 goto c_real_end_process;
609 }
610 else
611 {
612 socket_printf(p->clientsocket,"%s BAD Command unrecognized/login please: %s\r\n",parsed_message_get_idtag(&pm),parsed_message_get_command(&pm));
613 goto c_real_end_process;
614 }
615
616 c_real_end_process:
617 delete_parsed_message(&pm);
618 return 0;
619
620 c_send_bye:
621 /*
622 * something happened and we need to send a bye to the client.
623 * this means :
624 * send "BYE"
625 * close the socket
626 * set the correct states
627 * clean the messages
628 *
629 * since we can only be here if the client didn't authenticate yet,
630 * it means the state is DB_STATE_RECORD_TEMPORARY.
631 * this also means that there is no lasting connection to the server yet
632 * so, close the connection to the server as well, and remove the record all together.
633 *
634 */
635 socket_printf(p->clientsocket,"* BYE " PROGNAME " " VERSION " closing connection.\r\n");
636 delete_parsed_message(&pm);
637 debug("parent error occured\n");
638
639 db_set_misc_state(p,DB_STATE_MISC_REMOVE_RECORD);
640 return -1;
641 }
642
643 /* process server message */
parent_process_server_message(DB_record * p)644 int parent_process_server_message(DB_record *p)
645 {
646 struct message_t *currmessage = NULL;
647 int i = 0;
648 int argcount = 0;
649 char *tmp = NULL;
650 //Split_string ss;
651 Parsed_message pm;
652
653 currmessage = &(p->servermessage);
654
655 if(config_enable_debug)
656 {
657 for(i = 0;i < currmessage->count; i++)
658 {
659 if((tmp = db_get_server_message(p,i)))
660 {
661 if(!i)
662 debug("[PS:] %.80s%s",tmp,strlen(tmp)>80?"...\n":"");
663 else
664 debug("[PS:] ...\n");
665 }
666 }
667 }
668
669 if(!(db_get_record_state(p) == DB_STATE_RECORD_ACTIVE || db_get_record_state(p) == DB_STATE_RECORD_INACTIVE || db_get_record_state(p) == DB_STATE_RECORD_TEMPORARY))
670 {
671 debug("parent_process_server_message(): We are in an impossible record state !!! %s\n",db_get_state_string(db_get_record_state(p)));
672 return -1;
673 }
674
675 if(db_get_record_state(p) == DB_STATE_RECORD_INACTIVE)
676 {
677 /* if server sends a BYE, it will close the connection, and we'll clean it up in the next select() */
678 return 0;
679 }
680
681 if(db_get_record_state(p) == DB_STATE_RECORD_ACTIVE)
682 {
683 debug("parent_process_server_message(): We should not be in DB_STATE_RECORD_ACTIVE state !! Did we forget to fork ?.\n");
684 return -1;
685 }
686
687 /* we're now in TEMPORARY state */
688
689 init_parsed_message(&pm);
690 make_parsed_message(currmessage,&pm);
691 print_parsed_message(&pm);
692
693 if((argcount = parsed_message_count_args(&pm)) < 1)
694 {
695 debug("parent_process_server_message(): Server sent empty message\n");
696 log(LOG_WARNING,"%s Server sent empty message\n",db_get_record_info(p));
697 goto s_send_bye;
698 }
699
700 if(db_get_server_state(p) != DB_STATE_SERVER_NONE && db_get_proxy_state(p) != DB_STATE_PROXY_SENT_CAPABILITY_COMMAND_TO_SERVER && !strcmp(parsed_message_get_idtag(&pm),"*"))
701 {
702 /* if we are not expecting a greeting or the reply to the capability command, just treat this as a general notice */
703 goto s_end_process;
704 }
705
706 if(db_get_server_state(p) == DB_STATE_SERVER_NONE)
707 {
708 char *respcode = NULL;
709 if(argcount < 2)
710 {
711 debug("parent_process_server_message(): Server greeting is not long enough.\n");
712 log(LOG_WARNING,"%s Server greeting is not long enough.\n",db_get_record_info(p));
713 goto s_send_bye;
714 }
715
716 /* condition is true if :
717 * the first argument is not "*"
718 * or
719 * the second argument is not one of "OK" "PREAUTH" "BYE"
720 */
721 if(!(!strcmp(parsed_message_get_idtag(&pm),"*") && (!strcasecmp(parsed_message_get_command(&pm),"OK") ||
722 !strcasecmp(parsed_message_get_command(&pm),"PREAUTH") ||
723 !strcasecmp(parsed_message_get_command(&pm),"BYE"))))
724 {
725 debug("parent_process_server_message(): Couldn't understand server (%s %s ...)\n",parsed_message_get_idtag(&pm),parsed_message_get_command(&pm));
726 log(LOG_WARNING,"%s Server sent weird command in DB_STATE_SERVER_NONE\n",db_get_record_info(p));
727 goto s_send_bye;
728 }
729
730 /* process the response code, because we care (really) */
731
732 respcode = parsed_message_get_command(&pm);
733
734 if(!strcasecmp(respcode,"BYE"))
735 {
736 debug("parent_process_server_message(): Server sent a BYE ! should expect a panic close\n");
737 log(LOG_NOTICE,"%s Server sent BYE to proxy !\n",db_get_record_info(p));
738 goto s_send_bye;
739 }
740
741 /* otherwise, it's OK or PREAUTH */
742
743 db_set_server_state(p,DB_STATE_SERVER_SENT_GREETING);
744
745 debug("[PARENT SERVER PROCESS] Server sent Greeting\n");
746
747 socket_printf(p->serversocket,"0 CAPABILITY\r\n");
748 debug("parent_process_server_message(): We sent CAPABILITY command\n");
749 if(!(p->proxyauthtag = strdup("0")))
750 {
751 debug("parent_process_server_message(): Out of memory !\n");
752 log(LOG_CRIT,"%s Out of memory\n",db_get_record_info(p));
753 goto s_send_bye;
754 }
755
756 db_set_proxy_state(p,DB_STATE_PROXY_SENT_CAPABILITY_COMMAND_TO_SERVER);
757
758 goto s_end_process;
759 }
760 else if(db_get_server_state(p) == DB_STATE_SERVER_SENT_GREETING && db_get_proxy_state(p) == DB_STATE_PROXY_SENT_CAPABILITY_COMMAND_TO_SERVER)
761 {
762 /* first check if its an untagged response ! if it's not, well, go on */
763
764 if(!strcmp("*",parsed_message_get_idtag(&pm)))
765 {
766 /* check if there is more than 1 word, and if so, if the second is "CAPABILITY" */
767 if(!((argcount > 1) && !strcasecmp("CAPABILITY",parsed_message_get_command(&pm))))
768 {
769 /* no it isn't ...
770 * silently leave without changing any states */
771 goto s_end_process;
772 }
773
774 /* check if we can use authenticate login */
775 if(parent_can_use_authenticate_login(&pm))
776 {
777 db_set_capability_state(p,DB_STATE_CAPABILITY_AUTH_LOGIN);
778 }
779 else
780 {
781 db_set_capability_state(p,DB_STATE_CAPABILITY_LOGIN);
782 }
783
784 goto s_end_process;
785 }
786
787 if(!p->proxyauthtag)
788 {
789 debug("parent_process_server_message(): No recorded ID tag ?? This should not happen\n");
790 goto s_send_bye;
791 }
792
793 /* check if the given ID tag matches our request */
794 if(strcmp(p->proxyauthtag,parsed_message_get_idtag(&pm)))
795 {
796 debug("parent_process_server_message(): Server response does not match recorded client capability tag ! (\"%s\" <> \"%s\")\n",
797 parsed_message_get_idtag(&pm),p->clientauthtag);
798 log(LOG_WARNING,"%s Server returned an unknown ID tag\n",db_get_record_info(p));
799 goto s_send_bye;
800 }
801
802 /* free it */
803 free(p->proxyauthtag);
804 p->proxyauthtag = NULL;
805
806 /* check for weird conditions (maybe the server sent NO ?) */
807
808 if(strcasecmp(parsed_message_get_command(&pm),"OK"))
809 {
810 debug("parent_process_server_message(): Server did not like CAPABILITY command ?!\n");
811 log(LOG_WARNING,"%s Server did not like CAPABILITY command.\n",db_get_record_info(p));
812 goto s_send_bye;
813 }
814
815 /* check whether we can use authenticate login */
816
817
818
819 if(db_get_capability_state(p) == DB_STATE_CAPABILITY_AUTH_LOGIN)
820 {
821 socket_printf(p->serversocket,"1 AUTHENTICATE LOGIN\r\n");
822 }
823 else
824 {
825 /* we send the username and password as literals.
826 * this way, we avoid trouble with weird passwords that have \r\n in them and things
827 *
828 */
829 socket_printf(p->serversocket,"1 LOGIN {%d}\r\n",strlen(p->username));
830 }
831
832 /* we'll be using the same states for both authenticate login and the normal login
833 * to remember which method we used, later on
834 * we just have to look at the capability state.
835 */
836 db_set_proxy_state(p,DB_STATE_PROXY_SENT_AUTHENTICATE_COMMAND_TO_SERVER);
837 db_set_server_state(p,DB_STATE_SERVER_SENT_CAPABILITY_REPLY);
838
839 /* remember the ID tag we used */
840 if(!(p->proxyauthtag = strdup("1")))
841 {
842 debug("parent_process_server_message(): Out of memory !\n");
843 log(LOG_CRIT,"%s Out of memory\n",db_get_record_info(p));
844 goto s_send_bye;
845 }
846
847 goto s_end_process;
848 }
849 else if(db_get_server_state(p) == DB_STATE_SERVER_SENT_CAPABILITY_REPLY && db_get_proxy_state(p) == DB_STATE_PROXY_SENT_AUTHENTICATE_COMMAND_TO_SERVER)
850 {
851 char buffer[DB_STRING_LENGTH+1];
852
853 if(strcmp(parsed_message_get_idtag(&pm),"+"))
854 {
855 debug("parent_process_server_message(): Couldn't understand server (%s ...)\n",parsed_message_get_idtag(&pm));
856 log(LOG_WARNING,"%s Server didn't continue with continuation of AUTHENTICATE command\n",db_get_record_info(p));
857 goto s_send_bye;
858 }
859 #if 0
860 base64decode(buffer,parsed_message_get_command(&pm),sizeof(buffer));
861
862 /* Server should now send "User Name" or maybe "Username", as the rfc is not specific about this... */
863 if(strcasecmp(buffer,"user name") && strcasecmp(buffer,"username"))
864 {
865 debug("parent_process_server_message(): Couldn't understand server (+ %s ...) (Should send username)\n",buffer);
866 log(LOG_WARNING,"Server did not sent \"User Name\" or \"Username\" in base64 as a continuation command\n");
867 goto s_send_bye;
868 }
869 #endif
870
871 db_set_server_state(p,DB_STATE_SERVER_SENT_USERNAME_REPLY);
872 debug("[PARENT SERVER PROCESS] Server sent continuation for username\n");
873
874 if(db_get_capability_state(p) == DB_STATE_CAPABILITY_AUTH_LOGIN)
875 {
876 memset(buffer,0,sizeof(buffer));
877 base64encode(buffer,p->username,strlen(p->username)+1);
878
879 socket_printf(p->serversocket,"%s\r\n",buffer);
880 }
881 else
882 {
883 socket_printf(p->serversocket,"%s {%d}\r\n",p->username,strlen(p->password));
884 }
885
886 db_set_proxy_state(p,DB_STATE_PROXY_SENT_USERNAME_TO_SERVER);
887
888 goto s_end_process;
889 }
890 else if(db_get_server_state(p) == DB_STATE_SERVER_SENT_USERNAME_REPLY && db_get_proxy_state(p) == DB_STATE_PROXY_SENT_USERNAME_TO_SERVER)
891 {
892 char buffer[DB_STRING_LENGTH+1];
893
894 if(strcmp(parsed_message_get_idtag(&pm),"+"))
895 {
896 debug("parent_process_server_message(): Couldn't understand server (%s ...)\n",parsed_message_get_idtag(&pm));
897 log(LOG_WARNING,"%s Server didn't continue with continuation of AUTHENTICATE command\n",db_get_record_info(p));
898 goto s_send_bye;
899 }
900
901 #if 0
902 base64decode(buffer,parsed_message_get_command(&pm),sizeof(buffer));
903
904 if(strcasecmp(buffer,"password"))
905 {
906 debug("parent_process_server_message(): Couldn't understand server (+ %s ...) (Should send username)\n",buffer);
907 log(LOG_WARNING,"Server did not sent \"Password\" in base64 as a continuation command\n");
908 goto s_send_bye;
909 }
910
911 #endif
912
913 db_set_server_state(p,DB_STATE_SERVER_SENT_PASSWORD_REPLY);
914
915 debug("[PARENT SERVER PROCESS] Server sent continuation for password\n");
916
917 if(db_get_capability_state(p) == DB_STATE_CAPABILITY_AUTH_LOGIN)
918 {
919 memset(buffer,0,sizeof(buffer));
920 base64encode(buffer,p->password,strlen(p->password)+1);
921 socket_printf(p->serversocket,"%s\r\n",buffer);
922 }
923 else
924 {
925 socket_printf(p->serversocket,"%s\r\n",p->password);
926 }
927
928 db_set_proxy_state(p,DB_STATE_PROXY_SENT_PASSWORD_TO_SERVER);
929
930 goto s_end_process;
931 }
932 else if(db_get_server_state(p) == DB_STATE_SERVER_SENT_PASSWORD_REPLY && db_get_proxy_state(p) == DB_STATE_PROXY_SENT_PASSWORD_TO_SERVER)
933 {
934 char *tmp;
935
936 if(!p->proxyauthtag)
937 {
938 debug("parent_process_server_message(): No proxy auth tag recorded !\n");
939 log(LOG_WARNING,"%s Proxy didn't send ID tag ??\n",db_get_record_info(p));
940 goto s_send_bye;
941 }
942
943 if(strcmp(parsed_message_get_idtag(&pm),p->proxyauthtag))
944 {
945 debug("parent_process_server_message(): Server response does not match recorded client auth tag ! (\"%s\" <> \"%s\")\n",parsed_message_get_idtag(&pm),
946 p->clientauthtag);
947 /* untagged response from server ?? */
948 log(LOG_WARNING,"%s Server returned an unknown ID tag\n",db_get_record_info(p));
949 goto s_send_bye;
950 }
951
952 free(p->proxyauthtag);
953 p->proxyauthtag = NULL;
954
955 /* this is a message with the response code of the authentication login */
956 /* possible replies : OK, NO, BAD */
957
958 tmp = parsed_message_get_command(&pm);
959 if(!strcasecmp(tmp,"OK"))
960 {
961 debug("[PARENT SERVER PROCESS] Server says login ok\n");
962 socket_printf(p->clientsocket,"%s OK\r\n",p->clientauthtag);
963 db_set_client_state(p,DB_STATE_CLIENT_AUTHENTICATED);
964 db_set_proxy_state(p,DB_STATE_PROXY_SENT_LOGIN_OK_TO_CLIENT);
965 db_set_server_state(p,DB_STATE_SERVER_SENT_LOGIN_OK);
966 db_set_record_state(p,DB_STATE_RECORD_READY_TO_FORK);
967 log(LOG_INFO,"%s User \"%s\" logged in successfully.\n",db_get_record_info(p),p->username);
968 goto s_end_process;
969 }
970 else if(!strcasecmp(tmp,"NO"))
971 {
972 debug("[PARENT SERVER PROCESS] Server says login failed\n");
973 socket_printf(p->clientsocket,"%s NO\r\n",p->clientauthtag);
974 db_set_proxy_state(p,DB_STATE_PROXY_SENT_LOGIN_FAILED_TO_CLIENT);
975 log(LOG_INFO,"%s User \"%s\" failed login.\n",db_get_record_info(p),p->username);
976 /* remove this record since it was temporary ! */
977 goto s_send_bye;
978 }
979 else if(!strcasecmp(tmp,"BAD"))
980 {
981 debug("[PARENTSERVER PROCESS] Server claims bad command\n");
982 socket_printf(p->clientsocket,"%s BAD\r\n",p->clientauthtag);
983 db_set_proxy_state(p,DB_STATE_PROXY_SENT_LOGIN_FAILED_TO_CLIENT);
984 db_set_server_state(p,DB_STATE_SERVER_SENT_GREETING);
985
986 log(LOG_CRIT,"%s Server send BAD reply to login attempt.\n",db_get_record_info(p));
987 /* remove this record since it was temporary ! */
988 goto s_send_bye;
989 }
990
991 debug("parent_process_server_message(): Server sent some weird reply (%s %s ...)\n",parsed_message_get_idtag(&pm),tmp);
992 log(LOG_WARNING,"%s Server sent something weird\n",db_get_record_info(p));
993 goto s_send_bye;
994 }
995
996 s_end_process:
997 delete_parsed_message(&pm);
998 return 0;
999
1000 s_send_bye:
1001
1002 /*
1003 * if we get here, it means we can close the client and server socket, and free the record
1004 * no matter what the record state is
1005 */
1006
1007 /* provide client and server with a clean exit */
1008 socket_printf(p->clientsocket,"* BYE " PROGNAME " " VERSION " closing connection.\r\n");
1009 socket_printf(p->serversocket,"0 LOGOUT\r\n");
1010
1011 delete_parsed_message(&pm);
1012 debug("parent error occured\n");
1013
1014 db_set_misc_state(p,DB_STATE_MISC_REMOVE_RECORD);
1015 return -1;
1016
1017 }
1018
1019 /* handle new data and parse it into a message */
parent_handle_connection_new_data(DB_record * p,char * buffer,int len,int what)1020 int parent_handle_connection_new_data(DB_record *p,char *buffer,int len,int what)
1021 {
1022 struct message_t *currmessage = NULL;
1023 char *tmpbuf = NULL, *tmpp = NULL;
1024 char *buf = NULL;
1025 int tmpi = 0;
1026 int n = 0;
1027
1028 /* new data, store it in buffer
1029 * handle the data if it is complete (contains \r\n)
1030 */
1031
1032 if(what == INDEX_FOR_SERVER)
1033 currmessage = &(p->servermessage);
1034 else
1035 currmessage = &(p->clientmessage);
1036
1037 buf = buffer;
1038
1039 while(len > 0)
1040 {
1041 if(currmessage->literalcountdown > 0)
1042 {
1043 /* we are in literal mode
1044 * meaning : append everything to the message, untill we the literalcountdown is 0
1045 */
1046 if(currmessage->literalcountdown < len)
1047 {
1048 n = currmessage->literalcountdown;
1049 if(!(tmpbuf = (char *)calloc(n+1,sizeof(char))))
1050 {
1051 debug("parent_handle_connection_new_data(): Out of memory !\n");
1052 return -1;
1053 }
1054
1055 strncpy(tmpbuf,buf,n);
1056
1057 if(what == INDEX_FOR_SERVER)
1058 db_append_to_server_message(p,tmpbuf);
1059 else
1060 db_append_to_client_message(p,tmpbuf);
1061
1062 free(tmpbuf);
1063 tmpbuf = NULL;
1064 }
1065 else
1066 {
1067 n = len;
1068 if(what == INDEX_FOR_SERVER)
1069 db_append_to_server_message(p,buf);
1070 else
1071 db_append_to_client_message(p,buf);
1072 }
1073
1074 /* change literal countdown */
1075 currmessage->literalcountdown -= n;
1076
1077 /* update pointer in current buffer */
1078 buf += n;
1079 len -= n;
1080
1081 }
1082 else
1083 {
1084 /* we are in normal mode
1085 * meaning: whatever lies ahead, process it normally
1086 */
1087
1088 /* find \r\n */
1089 tmpp = strstr(buf,"\r\n");
1090
1091 if(tmpp)
1092 {
1093 /* a command ends in this line
1094 *
1095 * there are 2 options:
1096 * * the last word is a {123} literal header -> a literal starts and the message doesn't end
1097 * * no {123} -> the command is complete, and the message ends
1098 */
1099
1100 /* append to the message */
1101 n = tmpp - buf + strlen("\r\n");
1102
1103 if(!(tmpbuf = (char *)calloc(n+1,sizeof(char))))
1104 {
1105 debug("parent_handle_connection_new_data(): Out of memory !\n");
1106 return -1;
1107 }
1108
1109 strncpy(tmpbuf,buf,n);
1110
1111 if(what == INDEX_FOR_SERVER)
1112 db_append_to_server_message(p,tmpbuf);
1113 else
1114 db_append_to_client_message(p,tmpbuf);
1115
1116 /* now that it is appended, check for {123} */
1117
1118 if((tmpi = check_for_literal_count(tmpbuf)) < 0)
1119 {
1120 /* no literal count -> process and delete message */
1121 if(what == INDEX_FOR_SERVER)
1122 {
1123 parent_process_server_message(p);
1124 db_delete_server_message(p);
1125 }
1126 else
1127 {
1128 parent_process_client_message(p);
1129 db_delete_client_message(p);
1130 }
1131 }
1132 else
1133 {
1134 /* start of literal -> set all vars, and jump back in the loop */
1135 currmessage->literalcountdown = tmpi;
1136
1137 if(what == INDEX_FOR_SERVER)
1138 {
1139 db_create_new_line_in_server_message(p);
1140 }
1141 else
1142 {
1143 /* server sends a continuation request for each literal */
1144 db_create_new_line_in_client_message(p);
1145 socket_printf(p->clientsocket,"+ Ready for argument\r\n");
1146 }
1147 }
1148
1149 free(tmpbuf);
1150 tmpbuf = NULL;
1151 len -= n;
1152 buf += n;
1153
1154 }
1155 else
1156 {
1157 /* no \r\n
1158 * just plain old regular data, append to the message
1159 */
1160
1161 if(what == INDEX_FOR_SERVER)
1162 db_append_to_server_message(p,buf);
1163 else
1164 db_append_to_client_message(p,buf);
1165
1166 len -= strlen(buf);
1167 buf += strlen(buf);
1168 }
1169 }
1170 }
1171
1172 return 0;
1173 }
1174
parent_handle_client_connection_new_data(DB_record * p,char * buffer,int len)1175 int parent_handle_client_connection_new_data(DB_record *p,char *buffer,int len)
1176 {
1177 return parent_handle_connection_new_data(p,buffer,len,INDEX_FOR_CLIENT);
1178 }
1179
parent_handle_server_connection_closed(DB_record * p)1180 int parent_handle_server_connection_closed(DB_record *p)
1181 {
1182 /* server connection closed, close client connection if any, and remove from database */
1183
1184 debug("[PARENT SERVER CLOSED] <%d>\n",p->serversocket);
1185
1186 /* inform only when a client is connected */
1187 if(p->clientsocket >= 0)
1188 {
1189 socket_printf(p->clientsocket,"* BYE\r\n");
1190 }
1191
1192 db_set_misc_state(p,DB_STATE_MISC_REMOVE_RECORD);
1193 return 0;
1194 }
1195
parent_handle_server_connection_error(DB_record * p)1196 int parent_handle_server_connection_error(DB_record *p)
1197 {
1198 debug("parent_handle_server_connection_error(): read() error on socket %d (%m)\n",p->serversocket);
1199
1200 /* inform only when a client is connected */
1201 if(p->clientsocket >= 0)
1202 {
1203 socket_printf(p->clientsocket,"* BYE\r\n");
1204 }
1205
1206 db_set_misc_state(p,DB_STATE_MISC_REMOVE_RECORD);
1207 return 0;
1208 }
1209
parent_handle_server_connection_new_data(DB_record * p,char * buffer,int len)1210 int parent_handle_server_connection_new_data(DB_record *p,char *buffer,int len)
1211 {
1212 return parent_handle_connection_new_data(p,buffer,len,INDEX_FOR_SERVER);
1213 }
1214
parent_conversate(int fd)1215 int parent_conversate(int fd)
1216 {
1217 char buffer[1024];
1218 fd_set readset, writeset;
1219 struct timeval tv;
1220
1221 int r = 0, rr = 0, maxfd = 0;
1222 DB_record *p,*q;
1223
1224 bzero(buffer,sizeof(buffer));
1225 FD_ZERO(&readset);
1226 FD_ZERO(&writeset);
1227 FD_SET(fd,&readset);
1228
1229 #define SELECT_TIMEOUT_VALUE 60
1230 tv.tv_sec = SELECT_TIMEOUT_VALUE;
1231 tv.tv_usec = 0;
1232
1233 maxfd = fd;
1234
1235
1236 /* check if we got a reload signal */
1237 if(rehash_flag)
1238 {
1239 rehash_flag = 0;
1240
1241 debug("Reloading configfile...\n");
1242
1243 if(config_file_read(option_config_file))
1244 {
1245 debug("Error loading configfile !\n");
1246 exit(0);
1247 }
1248 }
1249
1250
1251 /* add each socket from the records, to the readfdset */
1252 for(p = DB_start; p != NULL; p = p->next)
1253 {
1254 if(db_get_misc_state(p) == DB_STATE_MISC_REMOVE_RECORD)
1255 {
1256 /* do nothing ! */
1257 }
1258 else
1259 if(db_get_record_state(p) == DB_STATE_RECORD_INACTIVE)
1260 {
1261 /* if the server sends anything besides a close, throw it away
1262 */
1263 FD_SET(p->serversocket,&readset);
1264 if(p->serversocket > maxfd)
1265 maxfd = p->serversocket;
1266 }
1267 else
1268 /* if the session is forked, only add the *pipe* to the readset
1269 */
1270 if(db_get_record_state(p) == DB_STATE_RECORD_ACTIVE)
1271 {
1272 /* record forked, so keep an eye on the ipc pipe */
1273 FD_SET(p->ipcfd[0],&readset);
1274 if(p->ipcfd[0] > maxfd)
1275 maxfd = p->ipcfd[0];
1276 }
1277 else
1278 {
1279 /* DB_STATE_RECORD_TEMPORARY */
1280
1281 FD_SET(p->clientsocket,&readset);
1282 if(p->clientsocket > maxfd)
1283 maxfd = p->clientsocket;
1284
1285 /* we need to know if the nonblocking server-socket has connected successfully
1286 * only add to writeset if serversocket is in non-connected mode ! */
1287 if(db_get_server_connection_state(p) == DB_STATE_SERVER_CONNECTION_STARTED)
1288 FD_SET(p->serversocket,&writeset);
1289
1290 if(db_get_server_connection_state(p) != DB_STATE_SERVER_NOT_CONNECTED)
1291 {
1292 FD_SET(p->serversocket,&readset);
1293 if(p->serversocket > maxfd)
1294 maxfd = p->serversocket;
1295 }
1296 }
1297 }
1298
1299 maxfd++;
1300
1301 if((r = select(maxfd,&readset,&writeset,NULL,&tv)) > 0)
1302 {
1303 /* reset Globaltime, because select() might have been blocking for a while */
1304 Globaltime = time(NULL);
1305
1306 /* new connection ? */
1307 if(FD_ISSET(fd,&readset))
1308 {
1309 parent_handle_new_connection(fd);
1310 }
1311
1312 p = DB_start;
1313
1314 /* check the status of the socket for every record */
1315 while(p)
1316 {
1317 q = p->next;
1318
1319 /* first of all
1320 * if we marked another record than the current, for removal
1321 * (as is the case with a reactivation of an inactive record)
1322 * we need to check if the old record has the remove flag set.
1323 * if it has, then skip ahead to the remove part, and don't check it's sockets.
1324 */
1325
1326 if(db_get_misc_state(p) == DB_STATE_MISC_REMOVE_RECORD)
1327 {
1328 goto all_your_record_are_belong_to_us;
1329 }
1330
1331 /* IMPORTANT NOTE:
1332 * while checking the sockets, it is possible that a DB_record was removed.
1333 * so it is important not to do anything else with the DB_record, once its connections have been checked.
1334 */
1335
1336 /* if this is a forked record's parent, only check the ipcfd */
1337 if((db_get_record_state(p) == DB_STATE_RECORD_ACTIVE))
1338 {
1339 if(FD_ISSET(p->ipcfd[0],&readset))
1340 {
1341 int retcode = 0;
1342 int temppid = 0;
1343
1344 rr = read(p->ipcfd[0],&retcode,sizeof(int));
1345
1346 if(rr < 0)
1347 {
1348 debug("parent_conversate(): Error reading from ipc socket.\n");
1349 return -1;
1350 }
1351 else if(rr == 0)
1352 {
1353 debug("parent_conversate(): Child closed ipc socket.\n");
1354 /* kill child ! and remove structure. */
1355 debug("killing process ID %d because of closed connection\n",p->pid);
1356 kill(p->pid,9);
1357 p->pid = -1;
1358
1359 db_set_misc_state(p,DB_STATE_MISC_REMOVE_RECORD);
1360 }
1361 else
1362 {
1363 /* if we get here, it means that the forked process has finished.
1364 * this means the session is done.
1365 * so we can clean up the mess, and make the record available again
1366 * (if the server didn't close the connection)
1367 */
1368
1369 log(LOG_INFO,"%s Session for user \"%s\" terminated.\n",db_get_record_info(p),p->username);
1370 temppid = p->pid;
1371 debug("cleaning up record with pid %d\n",temppid);
1372 db_cleanup(p);
1373 debug("killing process ID %d\n",temppid);
1374 kill(temppid,9);
1375 db_set_record_state(p,DB_STATE_RECORD_INACTIVE);
1376
1377 switch(retcode)
1378 {
1379 case DB_IPC_RETURNCODE_NORMAL:
1380 debug("parent_conversate(): Child reports normal exit.\n");
1381 break;
1382 case DB_IPC_RETURNCODE_SERVER_CLOSED:
1383 debug("parent_conversate(): Child reports server closed connection.\n");
1384 close(p->serversocket);
1385 p->serversocket = -1;
1386 db_del(p);
1387 break;
1388 case DB_IPC_RETURNCODE_SERVER_ERROR:
1389 debug("parent_conversate(): Child reports error on server connection.\n");
1390 close(p->serversocket);
1391 p->serversocket = -1;
1392 db_del(p);
1393 break;
1394 case DB_IPC_RETURNCODE_CLIENT_CLOSED:
1395 debug("parent_conversate(): Child reports client closed connection.\n");
1396 break;
1397 case DB_IPC_RETURNCODE_CLIENT_ERROR:
1398 debug("parent_conversate(): Child reports error on client connection.\n");
1399 break;
1400 }
1401
1402 /* if we have used this record too often, remove it. */
1403 if(p->reuse >= config_max_reuse && p->serversocket != -1)
1404 {
1405 log(LOG_INFO,"%s Record has been reused %d times. Removing it...\n",db_get_record_info(p),p->reuse);
1406 socket_printf(p->serversocket,"0 LOGOUT\r\n");
1407 close(p->serversocket);
1408 db_del(p);
1409 }
1410 }
1411 }
1412 }
1413 else /* don't read from client socket unless we are a temporary record */
1414 if((db_get_record_state(p) == DB_STATE_RECORD_TEMPORARY) && FD_ISSET(p->clientsocket,&readset))
1415 {
1416 rr = read(p->clientsocket,buffer,sizeof(buffer)-1);
1417
1418 p->clientidlesince = Globaltime;
1419
1420 if(rr < 0)
1421 {
1422 parent_handle_client_connection_error(p);
1423 }
1424 else if(rr == 0)
1425 {
1426 parent_handle_client_connection_closed(p);
1427 }
1428 else
1429 {
1430 buffer[rr] = '\0';
1431 parent_handle_client_connection_new_data(p,buffer,rr);
1432 }
1433 }
1434 else /* only check if server is connected, if we started a connection, and if we are in temporary state */
1435 if((db_get_server_connection_state(p) == DB_STATE_SERVER_CONNECTION_STARTED) &&
1436 (db_get_record_state(p) == DB_STATE_RECORD_TEMPORARY) && FD_ISSET(p->serversocket,&writeset))
1437 {
1438 int sockval = 0;
1439 int ret;
1440 int len = sizeof(int);
1441
1442 ret = getsockopt(p->serversocket, SOL_SOCKET, SO_ERROR, &sockval, &len);
1443
1444 if(ret < 0)
1445 {
1446 /* panic and jump out of the window */
1447 debug("parent_conversate(): getsockopt error (%m) CLOSE\n");
1448
1449 /* tell client to leave */
1450 parent_handle_server_connection_closed(p);
1451
1452 db_set_misc_state(p,DB_STATE_MISC_REMOVE_RECORD);
1453 }
1454 else switch(sockval)
1455 {
1456 case 0:
1457 /* connected */
1458 debug("parent_conversate(): Server connected ! (client %d - server %d)\n",p->clientsocket,p->serversocket);
1459 db_set_server_connection_state(p, DB_STATE_SERVER_CONNECTED);
1460 break;
1461 default:
1462 /* not connected */
1463 debug("parent_conversate(): Connection to server failed. CLOSE\n");
1464 parent_handle_server_connection_closed(p);
1465 break;
1466
1467 }
1468 }
1469 else /* only read from the server socket if it is connected, and if the record is not active */
1470 if((db_get_server_connection_state(p) != DB_STATE_SERVER_NOT_CONNECTED) &&
1471 (db_get_record_state(p) != DB_STATE_RECORD_ACTIVE) && FD_ISSET(p->serversocket,&readset))
1472 {
1473 rr = read(p->serversocket,buffer,sizeof(buffer)-1);
1474
1475 if(rr < 0)
1476 {
1477 parent_handle_server_connection_error(p);
1478 }
1479 else if(rr == 0)
1480 {
1481 parent_handle_server_connection_closed(p);
1482 }
1483 else
1484 {
1485 buffer[rr] = '\0';
1486 parent_handle_server_connection_new_data(p,buffer,rr);
1487 }
1488 }
1489
1490 /* if client is authenticated, fork here */
1491 if( db_get_record_state(p) == DB_STATE_RECORD_READY_TO_FORK &&
1492 db_get_misc_state(p) != DB_STATE_MISC_REMOVE_RECORD &&
1493 db_get_client_state(p) == DB_STATE_CLIENT_AUTHENTICATED)
1494 {
1495 rr = parent_fork_record(p,fd);
1496 if(rr <= 0)
1497 return rr;
1498 }
1499
1500 all_your_record_are_belong_to_us:
1501
1502 /* if record was flagged for removal, then remove it ! */
1503 if(db_get_misc_state(p) == DB_STATE_MISC_REMOVE_RECORD)
1504 {
1505 if(p->pid > 0)
1506 {
1507 debug("parent_conversate(): while removing record, child still exists ? pid = %d\n",p->pid);
1508 }
1509
1510 db_del(p);
1511 }
1512
1513 p = q;
1514 }
1515 }
1516 else if(!r)
1517 {
1518 debug("parent_conversate(): select() timed out. (%d seconds without activity)\n",SELECT_TIMEOUT_VALUE);
1519 return 1;
1520 }
1521 else
1522 {
1523 if(errno != EINTR)
1524 {
1525 debug("parent_conversate(): select() error (%m)\n");
1526 return -1;
1527 }
1528 }
1529
1530 return -1;
1531 }
1532
1533
1534 /* this function checks for idle connections
1535 * and acts upon them
1536 *
1537 * we close idle client connections
1538 *
1539 * we send NOOP's over inactive serverconnections for some time
1540 * we close idle server connections after some other time
1541 */
parent_handle_idlers()1542 int parent_handle_idlers()
1543 {
1544 DB_record *p,*q;
1545
1546 p = DB_start;
1547
1548 while(p)
1549 {
1550 q = p->next;
1551
1552 if(db_get_record_state(p) == DB_STATE_RECORD_INACTIVE)
1553 {
1554 if(p->serveridlesince > 0 && (Globaltime - p->serveridlesince > config_server_timeout))
1555 {
1556 debug("parent_handle_idlers(): Server %d has been inactive for too long (%d s)... closing.\n",p->serversocket,Globaltime - p->serveridlesince);
1557 /* connection was open too long, gracefully close it */
1558 if(p->serversocket >= 0)
1559 {
1560 socket_printf(p->serversocket,"0 LOGOUT\r\n");
1561 }
1562
1563 db_del(p);
1564 }
1565 else
1566 if(p->serverlastnoop > 0 && (Globaltime - p->serverlastnoop > config_keepalive))
1567 {
1568 /* it's been too long since we last sent a NOOP
1569 */
1570
1571 debug("parent_handle_idlers(): Sending NOOP to server %d.\n",p->serversocket);
1572 socket_printf(p->serversocket,"0 NOOP\r\n");
1573 p->serverlastnoop = Globaltime;
1574 }
1575
1576 }
1577 else if(db_get_record_state(p) == DB_STATE_RECORD_TEMPORARY)
1578 {
1579 if(p->clientidlesince > 0 && (Globaltime - p->clientidlesince > config_client_timeout))
1580 {
1581 /* client is just sitting there, remove it */
1582 debug("parent_handle_idlers(): Client %d idle for too long: closing.\n",p->clientsocket);
1583
1584 if(p->clientsocket >= 0)
1585 {
1586 socket_printf(p->clientsocket,"* BYE Autologout after %d seconds.\r\n",config_client_timeout);
1587 }
1588
1589 db_del(p);
1590 }
1591 }
1592
1593 p = q;
1594 }
1595
1596 return 0;
1597 }
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610