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