OSDN Git Service

Ver.1.5.39 Fixed link error
[opengate/opengate.git] / opengate / opengatesrv / watch-client.c
1 /*************************************************
2 Opengate server
3   module for communication with client program (javascript) 
4
5 Copyright (C) 1999 Opengate Project Team
6 Written by Yoshiaki Watanabe 1999-2006
7 Modified Katsuhiko Eguchi, 2005 
8
9 This program is free software; you can redistribute it and/or
10 modify it under the terms of the GNU General Public License
11 as published by the Free Software Foundation; either version 2
12 of the License, or (at your option) any later version.
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
22
23 Email: watanaby@is.saga-u.ac.jp
24 **************************************************/
25
26 #include        "opengatesrv.h"
27
28 void GetPeerAddr(int sockfd, char *peerAddr);
29 void SendTerminateReply(void);
30 void ReadHttpHeaders(void);
31 void SendReplyToGetHello(void);
32 void SendHttpKeepPage(char *userid, char *sessionId, char *language, int port, int cookieAuth, char *redirectedUrl);
33 int SelectAccept(void);
34 void AcceptHttpReConnect(int timeout);
35 int MacAddrCheck(int ipStatus,char *clientAddr4, char *clientAddr6, char *macAddr4, char *macAddr6);
36 int IsRightKey(char *pNowKey, char *sessionId);
37 void SendHttpReply(char *reply);
38 void SendHttpNotFound(void);
39
40 void OnUsageTimeLimitAlarm(int signo);
41 void OnCheckBasicAlarm(int signo);
42 void OnCheckHttpAlarm(int signo);
43 void OnReadWaitAlarm(int signo);
44 void OnAjaxWaitAlarm(int signo);
45 void CheckBasic(void);
46 void CheckHttp(void);
47
48
49 extern char ruleNumber4[WORDMAXLN];  /* ipfw rule number in string form  */
50 extern char ruleNumber6[WORDMAXLN];  /* ip6fw rule number in string form */
51 extern char language[WORDMAXLN]; /* message language */
52
53 int ipType=IPV4;                     /* using IP type */
54 int listenfd[2]; /* file descriptor for listen port */
55 int hasSock6=TRUE; /* can get the socket for IPv6 */  
56 int connfd;   /* file descriptor for connection port */
57 int connectMode = NOCONNECT; /* the TCP connection mode */
58 char previousHello[BUFFMAXLN];  /* save previous hello request string */
59
60 struct AlarmArg{          /* arguments used in on-alarm functions */
61   struct clientAddr *pClientAddr;
62   char *clientAddr4;
63   char *macAddr4;
64   char *clientAddr6;
65   char *macAddr6;
66   char *userid;
67   char *userProperty;
68   int ipStatus;                    /* ipv4 ipv6 or dual */
69   int checkInterval;
70   int noPacketInterval;
71 } alarmArg;
72
73 int helloWait=FALSE;  /* hello reply waiting mode */
74 int readHelloTime=0;  /* the time of reading hello */
75 int sendHelloTime=0;  /* the time of sending hello */
76 int noReplyCount=0; /* count up the no reply to hello message */
77
78 int checkBasicAlarmRinged=FALSE;
79 int checkHttpAlarmRinged=FALSE;
80
81
82 /*******************************************************/
83 /* get temporary listen port of this server            */
84 /*  opengate uses specific server port for each client */
85 /*******************************************************/
86 int getListenPort(void)
87 {
88   struct sockaddr_in servaddr4;
89   struct sockaddr_in6 servaddr6;
90   extern const struct in6_addr in6addr_any;
91
92   int portNo;
93   int portmin;
94   int portmax;
95   
96   bzero(&servaddr4, sizeof(servaddr4));
97   bzero(&servaddr6, sizeof(servaddr6));
98
99   servaddr4.sin_family=AF_INET;
100   servaddr4.sin_addr.s_addr=htonl(INADDR_ANY);
101   
102   servaddr6.sin6_family=AF_INET6;
103   servaddr6.sin6_addr=in6addr_any;
104
105   /* get port search range from config file */
106   portmin=atoi(GetConfValue("ListenPort/Min"));
107   portmax=atoi(GetConfValue("ListenPort/Max"));
108   
109   /* search unused port between PORTMIN and PORTMAX */
110   for(portNo=portmin; portNo<=portmax; portNo++){
111     servaddr4.sin_port=htons(portNo);
112     servaddr6.sin6_port=htons(portNo);
113
114     listenfd[0]=Socket(AF_INET, SOCK_STREAM, 0);
115     listenfd[1]=Socket(AF_INET6, SOCK_STREAM, 0);
116
117     if(listenfd[0]<0) return -1; /* if error, return */
118     if(listenfd[1]<0) hasSock6=FALSE; /* IPv6 disabled */
119
120     if(hasSock6){
121       /* case of socket IPv6 is enabled */
122       if(listenfd[0]>=FD_SETSIZE && listenfd[1]>=FD_SETSIZE) return -1;
123       
124       if((bind(listenfd[0], (SA *)&servaddr4, sizeof(servaddr4))==0) &&
125          (bind(listenfd[1], (SA *)&servaddr6, sizeof(servaddr6))==0) ){
126         break;
127       }
128        
129       Close(listenfd[0]);
130       Close(listenfd[1]);
131     }
132     else{
133       /* case of socket IPv6 is disabled */
134       if(listenfd[0]>=FD_SETSIZE) return -1;
135       if(bind(listenfd[0], (SA *)&servaddr4, sizeof(servaddr4))==0)break;
136       Close(listenfd[0]);
137     }
138   }
139
140
141   if(portNo>portmax) return -1;  /* cannot get unused port */
142   
143   if(Listen(listenfd[0], LISTENQ)<0) return -1; /* if error, return */
144   if(hasSock6){
145     if(Listen(listenfd[1], LISTENQ)<0) return -1;
146   }
147   return portNo;
148 }
149
150 /******************************************************/
151 /* wait connection from client side Ajax type program */
152 /*  if no connection, return after some delay.        */
153 /* this function makes direct tcp connection to client*/
154 /* (not via web server) to keep long term connection  */
155 /******************************************************/
156 int waitClientConnect(char *userid, char *userProperty, char *sessionId, char *clientAddr4, char *clientAddr6, int duration, char *macAddr4, char *macAddr6, int ipStatus, struct clientAddr *pClientAddr, char *language, int port, int pid, int cookieAuth, char *redirectedUrl)
157 {
158   char buff[BUFFMAXLN];             /* read in buffer */
159   char connectAddr[ADDRMAXLN];      /* connected client address */
160   char httpStr[BUFFMAXLN];          /* HTTP GET string at terminate */
161   int timeSendHttpkeep=0;
162   int timeStart;
163
164   timeStart=time(NULL);
165
166   /* set arguments sending to alarm function */
167   alarmArg.pClientAddr=pClientAddr;
168   alarmArg.clientAddr4=clientAddr4;
169   alarmArg.clientAddr6=clientAddr6;
170   alarmArg.macAddr4=macAddr4;
171   alarmArg.macAddr6=macAddr6;
172   alarmArg.userid=userid;
173   alarmArg.userProperty=userProperty;
174   alarmArg.ipStatus=ipStatus;
175   alarmArg.checkInterval=atoi(GetConfValue("ActiveCheckInterval"));
176   alarmArg.noPacketInterval=atoi(GetConfValue("NoPacketInterval"));  
177
178   /* set no conection initially */
179   connectMode=NOCONNECT;
180
181   /* set the alarm for usage time limit */
182   /* (at no ajax connection, the gate is closed after the duration) */
183   AddAlarm("UsageTimeLimitAlarm",duration,FALSE,OnUsageTimeLimitAlarm);
184
185   /* set the alarm for periodic keep alive check */
186   AddAlarm("CheckBasicAlarm", alarmArg.checkInterval, FALSE, OnCheckBasicAlarm);
187
188   /* loop until accepting correct user */
189   /* the connectMode is changed to ENDCONNECT when UsageTimeLimitAlarm rings */
190   while(connectMode == NOCONNECT){
191
192     /* start alarms */
193     EnableAlarm();
194
195     /* wait connection in this function */
196     connfd = SelectAccept();
197
198     /* wait is eterminated when connection-arrived or alarm-ringed */
199     /* stop alarms*/
200     DisableAlarm();
201
202     /* negative return means fail-connection (an error/interupt occurs) */
203     if(connfd<0){
204       
205       /* periodic check */
206       if(checkBasicAlarmRinged) CheckBasic();
207
208       Close(connfd);
209       continue;
210     }
211
212     /* reaching this point means real-connection (not an error/interupt)  */
213     /* is it from the correct client addr (the check is skipped for IPv6) */
214     if(ipType==IPV4 && ipStatus!=IPV6ONLY){
215       GetPeerAddr(connfd, connectAddr);
216       if(isNull(connectAddr)||strcmp(connectAddr, clientAddr4)!=0){
217         Close(connfd);
218         continue;
219       }
220     }
221
222     /* reaching this point means real-connection from correct client  */
223     /* set read wait alarm */
224     AddAlarm("ReadWaitAlarm",atoi(GetConfValue("CommWaitTimeout")),
225              TRUE, OnReadWaitAlarm); 
226     EnableAlarm();
227
228     /* get string from connection */
229     if(readln(connfd, buff, BUFFMAXLN) <0){
230       /* if abnormal, wait next request */
231       Close(connfd);
232       continue;
233     }
234     RemoveAlarm("ReadWaitAlarm");
235     
236     /* is it the httpkeep page download request */
237     /* the request is [GET /httpkeep.html ....] */
238     snprintf(httpStr, BUFFMAXLN, "GET /httpkeep-%s-%s", userid,sessionId);
239     if(strstr(buff, httpStr)==buff){
240       
241       /* if so, read out the remained headers and send the httpkeep page */
242       ReadHttpHeaders();
243       SendHttpKeepPage(userid, sessionId, language, port, cookieAuth, 
244                        redirectedUrl);
245       timeSendHttpkeep=time(NULL);
246
247       /* return to wait next request (that night be GET /hello..) */
248       Close(connfd);
249       continue;
250     }
251
252     /* is it hello request from javascript in httpkeep.html */
253     if(strstr(buff, "GET /hello-")==buff){
254       
255       /* if so, read out the remained headers */
256       ReadHttpHeaders();
257
258       /* if the hello request is premature or too late, ignore the request */
259       if((timeSendHttpkeep == 0)||
260          (time(NULL)-timeSendHttpkeep)> atoi(GetConfValue("CommWaitTimeout"))){
261         Close(connfd);
262         continue;
263       }
264
265       /* if received key is incorrect, ignore the request */
266       /* [GET /hello-key1-key2 ..] */
267       if( IsRightKey(buff+strlen("GET /hello-"), sessionId)==FALSE){
268         Close(connfd);
269         continue;
270       }
271       
272       /* exit loop and enter to the Http watch mode. the connection is keeped (not closed) */
273       /* reply to the hello request are executed in WaitHttpClose routine */
274       connectMode=HTTPCONNECT;
275       break;
276     }
277
278     /* is it the termination request */
279     /* the request is [GET /terminate-<pid> ..] */
280     snprintf(httpStr, BUFFMAXLN, "GET /terminate-%d", pid);
281
282     /* if terminate request found, send reply and exit loop  */
283     if(strstr(buff, httpStr)==buff){
284       SendTerminateReply();
285       connectMode=ENDCONNECT;
286       Close(connfd);
287       break;
288     }
289
290     /* other request is ignored */
291     if(strstr(buff, "GET /")==buff){
292       ReadHttpHeaders();
293       SendHttpNotFound();
294     }
295     Close(connfd);
296     continue;
297   }
298
299   /* at exiting loop, stop all alarms */
300   RemoveAlarm(NULL);
301     
302   return connectMode;
303 }
304
305 /****************************/
306 /* wait for TCP connection  */
307 /****************************/
308 int selectAccept(void)
309 {
310   int connfd1 = -1;
311   int connfd2 = -1;
312   int smax;                         /* select max descliptor */
313   fd_set rfd0;                      /* fd_set for select */
314   int n;                            /* counter */
315   struct sockaddr_storage cliaddr;  /* client IP adddress */
316   socklen_t len = sizeof(cliaddr);
317
318   /* select socket */
319   FD_ZERO(&rfd0);
320   FD_SET(listenfd[0], &rfd0);
321   if(hasSock6) FD_SET(listenfd[1], &rfd0);
322
323   if(hasSock6){
324     if(listenfd[0]>listenfd[1]) smax=listenfd[0]+1;
325     else smax=listenfd[1]+1;
326   }else{
327     smax=listenfd[0]+1;
328   }
329
330   /* wait connection */
331   if((n = select(smax, &rfd0, NULL, NULL, NULL)) > 0){
332
333     /* connect by ipv4 */
334     if(FD_ISSET(listenfd[0], &rfd0)){
335       if((connfd1=accept(listenfd[0],(struct sockaddr *)&cliaddr, &len))>=0){
336         ipType=IPV4;
337       }
338     }
339
340     /* connect by ipv6 */
341     if(hasSock6){
342       if(FD_ISSET(listenfd[1], &rfd0)){
343         if((connfd2=accept(listenfd[1],(struct sockaddr *)&cliaddr, &len))>=0){
344           ipType=IPV6;
345         }
346       }
347     }
348   }
349   
350   if(connfd1>=0 && connfd2>=0){
351     Close(connfd2);
352     return connfd1;
353   }
354   else if(connfd1>=0 && connfd2<0) return connfd1;
355   else if(connfd1<0 && connfd2>=0) return connfd2;
356
357   return -1;
358 }
359
360 /******************************/
361 /* called at usage time limit */
362 /******************************/
363 void onUsageTimeLimitAlarm(int signo)
364 {
365   connectMode=ENDCONNECT;
366   err_msg("ERR at %s#%d: duration timeout",__FILE__,__LINE__);
367 }
368
369 /**********************************/
370 /* called at read wait time limit */
371 /**********************************/
372 void onReadWaitAlarm(int signo)
373 {
374   connectMode=ENDCONNECT;
375 }
376
377 /***************************************/
378 /* called at ajax request wait timeout */
379 /***************************************/
380 void onAjaxWaitAlarm(int signo)
381 {
382   connectMode=NOCONNECT;
383 }
384
385 /***************************************/
386 /* called at periodic alive basic check */
387 /***************************************/
388 void onCheckBasicAlarm(int signo)
389 {
390   checkBasicAlarmRinged=TRUE;
391 }
392
393 /******************************/
394 /* periodic alive basic check */
395 /******************************/
396 void checkBasic(void)
397 {
398   static int packetCountPrev=0;       /* packet count at previous check */
399   int packetCountNow=0;               /* packet count at now */
400   static int noPacketIntervalCount=0; /* no packet count in check loop */
401   static int firstCheck=TRUE;
402   int duplicateRule4,duplicateRule6;
403   int otherPid;
404
405   /* search new IPv6 addresses */
406   ScanNdpEntry(alarmArg.pClientAddr, alarmArg.userid,
407              alarmArg.macAddr6, alarmArg.userProperty);
408
409   /* mac address check */
410   if(MacAddrCheck(alarmArg.ipStatus, 
411                   alarmArg.clientAddr4, alarmArg.clientAddr6, 
412                   alarmArg.macAddr4, alarmArg.macAddr6)==FALSE){
413     connectMode=ENDCONNECT;
414     return;
415   }  
416
417   /* packet flow check */
418   packetCountNow=GetPacketCount(alarmArg.pClientAddr);
419   if(packetCountNow==packetCountPrev){  /* no packet between checks */
420     noPacketIntervalCount++;
421   }else{
422     noPacketIntervalCount=0;
423     packetCountPrev=packetCountNow;
424   }
425   
426   if(noPacketIntervalCount*alarmArg.checkInterval 
427      >= alarmArg.noPacketInterval){
428     err_msg("ERR at %s#%d: no packet passed for the client",
429             __FILE__,__LINE__);
430     connectMode=ENDCONNECT;
431     return;
432   }
433
434   /* duplicated session check */
435   /* this is added for ios behavior */
436   /* (duplication of ios web-auth and user web-auth) */
437   /* if dup, the process in basic mode is ended */
438   if(firstCheck){
439     firstCheck=FALSE;
440     if( FindDuplicateInDbAndClose(alarmArg.clientAddr4, 
441                                   &duplicateRule4, &duplicateRule6, &otherPid) ){
442       
443       /* the process exists (ret==0 means the existence) */
444       if(kill(otherPid, 0)==0){ 
445
446         /* remove my rules and return as duplicated */
447         RemoveDuplicateRule(duplicateRule4, duplicateRule6);
448         err_msg("ERR at %s#%d: duplicated ipfw rules for %s are removed",
449                 __FILE__,__LINE__, alarmArg.clientAddr4);
450         connectMode=DUPLICATED;
451       }
452
453       /* the process does not exist */
454       else{ 
455         FixProcessEndInDb(otherPid, "NONE");
456       }
457     }
458   }
459
460   /* set the alarm for next periodic keep alive check */
461   checkBasicAlarmRinged=FALSE;
462   AddAlarm("CheckBasicAlarm", alarmArg.checkInterval, FALSE, OnCheckBasicAlarm);  /* EnableAlarm is called automatically in alarm function */
463 }
464
465 /***************************************/
466 /* check mac address change            */
467 /***************************************/
468 int macAddrCheck(int ipStatus, char *clientAddr4, char *clientAddr6, char *macAddr4, char *macAddr6) 
469
470   char macAddrNow[ADDRMAXLN];       /* MAC address at now */
471
472   if(ipStatus!=IPV6ONLY){
473     /* check mac address from arp */
474     GetMacAddrFromArp(clientAddr4, macAddrNow);
475     if(*macAddrNow!='?' && strcmp(macAddr4, macAddrNow)!=0){
476       err_msg("ERR at %s#%d: mac address is changed",__FILE__,__LINE__);
477       connectMode=ENDCONNECT;
478       return FALSE;
479     }
480   }else{
481     /* check mac address from ndp */
482     GetMacAddrFromNdp(clientAddr6, macAddrNow);
483     if(*macAddrNow!='?' && strcmp(macAddr6, macAddrNow)!=0){
484       err_msg("ERR at %s#%d: mac address is changed",__FILE__,__LINE__);
485       connectMode=ENDCONNECT;
486       return FALSE;
487     }
488   }
489   return TRUE;
490 }
491
492 /******************************************/
493 /* get address of connected remote site   */
494 /******************************************/
495 void getPeerAddr(int sockfd, char *peerAddr)
496 {
497   struct sockaddr *cliaddr;
498   socklen_t len;
499   char *pAddr;
500
501   *peerAddr='\0'; /* set null string */
502
503   if((cliaddr=Malloc(ADDRMAXLN))==NULL) return; /* if error, return */
504
505   len=ADDRMAXLN;
506   if(Getpeername(sockfd, cliaddr, &len)<0) return; /* if error, return */
507     
508   pAddr=Sock_ntop_host(cliaddr, len);
509   if(pAddr!=NULL) strlcpy(peerAddr, pAddr, ADDRMAXLN);
510   
511   free(cliaddr);
512   
513   return;
514 }
515
516
517 /**********************************************************/
518 /* send reply to terminate access via connfd (not stdout) */
519 /* simulate web server response                           */
520 /**********************************************************/
521 void sendTerminateReply(void)
522 {
523   /* send HTTP headers */
524   Writefmt(connfd,"HTTP/1.1 200 OK\r\n");
525   Writefmt(connfd,"Connection: Close\r\n");
526   Writefmt(connfd,"Content-Type: text/html\r\n");
527   Writefmt(connfd,"Content-Length: 92\r\n");
528   Writefmt(connfd,"\r\n");
529   
530   Writefmt(connfd,"<META HTTP-EQUIV=Pragma CONTENT=no-cache>");
531   Writefmt(connfd,"<HTML><BODY> Network is closed. </BODY></HTML> \r\n\r\n");
532   Close(connfd);
533 }
534
535 /***************************************************/
536 /* send quit to client and close connection        */
537 /***************************************************/
538 void sendQuitClient(void)
539 {
540   Writefmt(connfd,"quit\r\n");
541   Close(connfd);
542 }
543
544 /************************************************************/
545 /* wait for close connection                                */
546 /* this function keeps direct tcp connection to client Ajax */
547 /* (not via web server)                                     */
548 /* execute periodic hello exchange in HTTP format           */
549 /************************************************************/
550 void waitHttpClose(struct clientAddr *pClientAddr, char *userid, char *userProperty, char *macAddr4, char *macAddr6, int ipStatus, char *sessionId, int port)
551 {
552   char buff[BUFFMAXLN];
553   int ret;
554
555   /* set alarm function arguments */
556   alarmArg.pClientAddr=pClientAddr;
557   alarmArg.macAddr4=macAddr4;
558   alarmArg.macAddr6=macAddr6;
559   alarmArg.userid=userid;
560   alarmArg.userProperty=userProperty;
561   alarmArg.ipStatus=ipStatus;
562   alarmArg.checkInterval=atoi(GetConfValue("ActiveCheckInterval"));
563
564   /* save the time of read hello */
565   readHelloTime = time(NULL);
566
567   /* send first reply to hello (corresponding request is in WaitClientConnect)*/
568   SendReplyToGetHello();
569   sendHelloTime = time(NULL);
570
571   /* set the hello wait mode ON */
572   helloWait=TRUE;
573   
574   /* TCP read/write loop */
575   /* this loop implement following logic */
576   /*  repeat until receiving 'GET /terminate' or EOF from client */
577   /*      wait request from client.                */
578   /*      if wait timeout,  then quit              */
579   /*      if 'GET /hello' request, then send reply */
580   /*      if 'GET /terminate' request,  then quit  */
581   /*      if connection closed, then quit          */
582   
583   /* set the alarm for periodic client check */
584   AddAlarm("checkHttpAlarm", alarmArg.checkInterval, FALSE, OnCheckHttpAlarm);
585   EnableAlarm();
586   
587     /* loop until end connection */  
588   while(connectMode!=ENDCONNECT){
589
590     /* set default to unknown */
591     connectMode=NOCONNECT;
592
593     /* read wait for client request */
594     ret=readln(connfd, buff, BUFFMAXLN);
595
596     /* at recieve, stop alarm between readin check*/
597     DisableAlarm();
598
599     if(ret>=0){
600       /* normal read */
601
602       /* is it [GET /hello] */
603       if(strstr(buff,"GET /hello") == buff){
604
605         /* if so, read out the remained headers */
606         ReadHttpHeaders();
607
608         /* if retry request (having same key as previous) */
609         if(strncmp(buff, previousHello, BUFFMAXLN)==0) {
610
611           /*SendReplyToGetHello();*/
612           /* some mulfunction on communication. reset connection */
613           connectMode=NOCONNECT;
614           Close(connfd);
615           AcceptHttpReConnect(atoi(GetConfValue("CommWaitTimeout")));
616           continue;
617         }
618
619         /* save hello request string including keys */    
620         strlcpy(previousHello, buff, BUFFMAXLN);
621
622         /* if received key is correct */
623         if(IsRightKey(buff+strlen("GET /hello-"), sessionId)){
624         
625           /* recognize HTTP hello request */
626           connectMode=HTTPCONNECT;
627         
628           /* save time to read hello */
629           readHelloTime = time(NULL);
630
631           /* set the hello wait mode OFF */
632           helloWait=FALSE;
633         }
634       
635         /* if key is incorrect, terminate */
636         else{
637           connectMode=ENDCONNECT;
638         }
639       }
640
641       /* read other string */
642       else{
643         
644         /* read request and reply not found */
645         if(strstr(buff,"GET /") == buff){
646           ReadHttpHeaders();
647           SendHttpNotFound();
648         }
649         err_msg("ERR at %s#%d: Unknown request",__FILE__,__LINE__);
650         connectMode=NOCONNECT;
651         Close(connfd);
652         AcceptHttpReConnect(atoi(GetConfValue("CommWaitTimeout")));
653       }
654     }
655
656     else{
657       /*abnormal read */
658       /* some alarm is ringed or connecion is closed */
659
660       /* periodic check */
661       if(checkHttpAlarmRinged) CheckHttp();
662
663       /* if no connection, retry connect */
664       if(connectMode==NOCONNECT){
665
666         /* wait short time to accept reconnection */
667         /* (to permit occasional disconnection at exchanging hello) */
668         Close(connfd);
669         if(helloWait==TRUE){
670           AcceptHttpReConnect(atoi(GetConfValue("ReconnectTimeout")));
671         }else{
672           AcceptHttpReConnect(atoi(GetConfValue("CommWaitTimeout")));
673         }
674       }
675     }
676
677     /* restart alarm */
678     EnableAlarm();
679   }
680
681   /* reset alarm */
682    RemoveAlarm(NULL);
683
684   Close(connfd);
685   
686   return;
687 }
688
689 /******************************************/
690 /* accept http reconnect                  */
691 /*  to cope with accidental disconnection */
692 /*  permit reconnection with short delay  */
693 /******************************************/
694 void acceptHttpReConnect(int timeout){
695
696   int startTime; 
697
698   startTime=time(NULL);
699
700   /* timeout value should be positive */
701   if(timeout<=0){
702     connectMode=ENDCONNECT;
703     return;
704   }
705
706   /* wait a short time */
707   AddAlarm("ReadWaitAlarm", timeout, TRUE, OnReadWaitAlarm); 
708   EnableAlarm();
709
710   /* connection wait */
711   connfd = SelectAccept();
712
713   /* receive request */
714   RemoveAlarm("ReadWaitAlarm");
715   if(connfd<0){  /* timeout or abnormal */
716     connectMode=ENDCONNECT;
717   }else{         /* reconnected */
718     connectMode=HTTPCONNECT;
719     RemoveAlarm("CheckHttpAlarm");
720     AddAlarm("CheckHttpAlarm", alarmArg.checkInterval, FALSE, OnCheckHttpAlarm);
721   }
722 }
723
724 /*****************************************************************/
725 /* simple override check by exchanging keys                      */ 
726 /* Is the sent client key correct                                */
727 /*  request string is as follows                                 */
728 /*  GET /hello-11111111111111111111-2222222222222222222 HTTP...  */
729 /*              MD5digest(32chars)   MD5digest(32chars)          */
730 /*                nowKey               nextKey                   */
731 /*                                                               */
732 /*         client                            server              */
733 /*       save sid     <-------------------     sid               */
734 /* md5(sid)+md5(md5(rand1)+sid)  ---------> check nowkey         */
735 /*                                          save nextKey         */
736 /* md5(rand1)+md5(md5(rand2)+sid)---------> check nowkey         */
737 /*                                          save nextKey         */
738 /* md5(rand2)+md5(md5(rand3)+sid)---------> check nowkey         */
739 /*                                          save nextKey         */
740 /*****************************************************************/
741 int isRightKey(char *arg, char *sessionId)
742 {
743   static char savedKey[33]=""; /* saved MD5 string */
744   char tempbuff[BUFFMAXLN];    /* work area */
745   char md5work[33]      ;      /* md5 work */
746   char *pNowKey;
747   char *pNextKey;
748
749   /* initial value of savedKey is md5(md5(sessionId)+sessionId) */
750   if(isNull(savedKey)){
751     md5hex(tempbuff, 33, sessionId);
752     strlcat(tempbuff, sessionId, BUFFMAXLN);
753     md5hex(savedKey, 33, tempbuff);
754   }
755
756   /* split NowKey and NextKey in argument */
757   /* 32 is the length of MD5 result */
758   pNowKey=arg;
759   *(pNowKey+32)='\0';
760   pNextKey=pNowKey+33;
761   *(pNextKey+32)='\0';
762
763   /* make string [nowKey+sessionId] */
764   strlcpy(tempbuff, pNowKey, BUFFMAXLN);
765   strlcat(tempbuff, sessionId, BUFFMAXLN);
766
767   /* compare savedKey and md5(nowKey+sessionId) */
768   if(strcmp(savedKey, md5hex(md5work, 33, tempbuff))==0){
769
770     /* save nextKey for next check */
771     strlcpy(savedKey, pNextKey, 33);
772     return TRUE;
773   }
774   else{
775     return FALSE;
776   }
777 }
778
779 /***************************************/
780 /* called at periodic http alive check */
781 /***************************************/
782 void onCheckHttpAlarm(int signo)
783 {
784   checkHttpAlarmRinged=TRUE;
785 }
786
787 /***************************************/
788 /* called at periodic http alive check */
789 /***************************************/
790 void checkHttp(void)
791 {
792   /* at this timing, hello request might be received */
793   /* if in hello wait mode, it is abnormal */
794   if(helloWait==TRUE){
795     connectMode=NOCONNECT;
796     return;
797   }
798
799   /* send delayed reply to hello request */
800   SendReplyToGetHello();
801
802   /* save time to send hello */
803   sendHelloTime = time(NULL);  
804
805   /* set the hello wait mode ON */
806   helloWait=TRUE;
807
808   /* search new IPv6 addresses */
809   ScanNdpEntry(alarmArg.pClientAddr, alarmArg.userid,
810              alarmArg.macAddr6, alarmArg.userProperty);
811
812   /* mac address check */
813   if(MacAddrCheck(alarmArg.ipStatus, 
814                   alarmArg.clientAddr4, alarmArg.clientAddr6, 
815                   alarmArg.macAddr4, alarmArg.macAddr6)==FALSE){
816     connectMode=ENDCONNECT;
817     return;
818   }
819
820   /* set the alarm for next periodic check */
821   checkHttpAlarmRinged=FALSE;
822   AddAlarm("CheckHttpAlarm", alarmArg.checkInterval, FALSE, OnCheckHttpAlarm);
823   /* EnableAlarm is called automatically in alarm function */
824
825   /* normal return */
826   connectMode = HTTPCONNECT;
827   return;
828 }
829
830 /***************************************************/
831 /* read and skip Http headers (ended by null line) */
832 /***************************************************/
833 void readHttpHeaders(void)
834 {
835   char buff[BUFFMAXLN];             /* read in buffer */
836   int n;
837
838   /* read until null line (only CRLF code) */
839   while((n=readln(connfd, buff, BUFFMAXLN))>0){
840     ;
841   }
842 }
843
844 /********************************************************/
845 /* send hello to client for replying GET /hello request */
846 /* simulate web server response in chunked mode         */
847 /********************************************************/
848 void sendReplyToGetHello(void)
849 {
850   /* send HTTP headers */
851   Writefmt(connfd,"HTTP/1.1 200 OK\r\n");
852   Writefmt(connfd,"Transfer-Encoding: chunked\r\n");
853   Writefmt(connfd,"Keep-Alive: timeout=300\r\n");
854   Writefmt(connfd,"Connection: Keep-Alive\r\n");
855   Writefmt(connfd,"Content-Type: text/html\r\n");
856   Writefmt(connfd,"\r\n");
857
858   /* send reply to hello */
859   Writefmt(connfd,"5\r\n");
860   Writefmt(connfd,"hello\r\n"); 
861
862   /* send end of chunk */
863   Writefmt(connfd,"0\r\n");
864   Writefmt(connfd,"\r\n");
865 }
866
867 /***************************************************/
868 /* send httpkeep page to client via connfd         */
869 /* after variables in template file are replaced   */
870 /* simulate web server response                    */
871 /***************************************************/
872 void sendHttpKeepPage(char *userid, char *sessionId, char *language, int port, int cookieAuth, char *redirectedUrl)
873 {
874   char buff[BUFFMAXLN];             /* read in buffer */
875   FILE *fp;
876
877   char httpKeepDoc[BUFFMAXLN];
878   char httpHelloUrl[BUFFMAXLN];
879   char terminateUrl[BUFFMAXLN];
880   char acceptDoc2Url[BUFFMAXLN];
881   char httpkeepJsUrl[BUFFMAXLN];
882   char md5JsUrl[BUFFMAXLN];
883   char portStr[WORDMAXLN];
884   char *startPageUrl="";
885   int startPageType=atoi(GetConfValue("StartPage/Type"));
886
887   char *opengateServerName=GetConfValue("OpengateServerName");
888   char *opengateDir=GetConfValue("OpengateDir");
889   char useridshort[USERMAXLN];
890   char extraId[USERMAXLN];
891
892   /* split id to display short format of userid */
893   SplitId(userid, useridshort, extraId);
894
895   /* create path to accept2 doc */
896   snprintf(acceptDoc2Url, BUFFMAXLN, 
897           "http://%s%s/%s/%s",opengateServerName,
898           opengateDir,language,GetConfValue("AcceptDoc2"));
899
900   /* create terminate url [http://<servaddr>:<port>/terminate-<pid>] */
901   snprintf(terminateUrl, BUFFMAXLN, "http://%s:%d/terminate-%d", 
902           opengateServerName, port, getpid());
903
904   /* create httphello url [http://<servaddr>:<port>/hello] */
905   snprintf(httpHelloUrl, BUFFMAXLN, "http://%s:%d/hello", 
906           opengateServerName, port);
907
908   /* create httpkeep.js url[http://<serveraddr>/opengate/httpkeep.js] */
909   snprintf(httpkeepJsUrl, BUFFMAXLN, "http://%s%s/%s", 
910            opengateServerName,
911            opengateDir,GetConfValue("HttpKeepJS"));
912
913   /* create md5.js url[http://<serveraddr>/opengate/md5.js] */
914   snprintf(md5JsUrl, BUFFMAXLN, "http://%s%s/%s", 
915            opengateServerName,
916            opengateDir,GetConfValue("Md5JS"));
917
918   /* create path to httpkeep doc */
919   snprintf(httpKeepDoc,BUFFMAXLN, "%s%s/%s/%s",GetConfValue("DocumentRoot"),
920           opengateDir,language,GetConfValue("HttpKeepDoc"));
921
922   /* create port string */
923   snprintf(portStr, WORDMAXLN, "%d", port);
924
925   /* open httpkeepdoc */
926   if((fp=fopen(httpKeepDoc, "r"))==NULL){
927     err_msg("ERR at %s#%d: cannot open %s",__FILE__,__LINE__,httpKeepDoc);
928     PutClientMsg("Cannot find html document");
929     return;
930   }
931
932   /* if redirect page is not set, use other setting */
933   if(isNull(redirectedUrl)){
934     if(isNull(GetConfValue("StartPage/Url"))) redirectedUrl=acceptDoc2Url;
935     else  redirectedUrl=GetConfValue("StartPage/Url");
936   }
937
938   /* create start page url to put information */
939   if(cookieAuth==1) startPageUrl=redirectedUrl;
940   else if(startPageType==0) startPageUrl=acceptDoc2Url;
941   else if(startPageType==1) startPageUrl=GetConfValue("StartPage/Url");
942   else if(startPageType==2) startPageUrl=redirectedUrl;
943   else startPageUrl=acceptDoc2Url;
944
945   /* send HTTP headers */
946   Writefmt(connfd,"HTTP/1.1 200 OK\r\n");
947   Writefmt(connfd,"Transfer-Encoding: chunked\r\n");
948   Writefmt(connfd,"Keep-Alive: timeout=300\r\n");
949   Writefmt(connfd,"Connection: Keep-Alive\r\n");
950   Writefmt(connfd,"Content-Type: text/html\r\n");
951   Writefmt(connfd,"\r\n");
952
953   while(fgets(buff, BUFFMAXLN, fp)!=NULL){
954
955     /* length check */
956     if(strlen(buff)>=BUFFMAXLN-1){
957       err_msg("ERR at %s#%d: too long line in %s",__FILE__,__LINE__,
958               httpKeepDoc);
959     }
960
961     /* replace mark */
962     htmlReplace(buff, "%%OPENGATESERVERNAME%%", opengateServerName);
963     htmlReplace(buff, "%%HTTPHELLOURL%%", httpHelloUrl);
964     htmlReplace(buff, "%%USERID%%", useridshort);
965     htmlReplace(buff, "%%SESSIONID%%", sessionId);
966     htmlReplace(buff, "%%TERMINATEURL%%", terminateUrl);
967     htmlReplace(buff, "%%HTTPKEEPJSURL%%", httpkeepJsUrl);
968     htmlReplace(buff, "%%MD5JSURL%%", md5JsUrl);
969
970     htmlReplace(buff, "%%OPENGATEDIR%%", opengateDir);
971     htmlReplace(buff, "%%OPENGATEPORT%%", portStr);
972     htmlReplace(buff, "%%LANGUAGE%%", language);
973     htmlReplace(buff, "%%STARTURL%%", startPageUrl);
974     htmlReplace(buff, "%%COOKIENAME%%", COOKIENAME);
975     htmlReplace(buff, "%%REDIRECTEDURL%%", redirectedUrl);
976
977     /* length of chunk in hex */
978     Writefmt(connfd, "%x\r\n", strlen(buff));
979     /* the chunk content */
980     Writefmt(connfd, "%s\r\n", buff);
981   }
982   
983   /* chunk end */
984   Writefmt(connfd,"0\r\n");
985   Writefmt(connfd,"\r\n");
986
987   fclose(fp);
988 }
989
990
991 /*************************************************************/
992 /* send reply to unknown http request eg:[GET /favico.ico..] */
993 /* simulate web server response in chunked mode              */
994 /*************************************************************/
995 void sendHttpReply(char *reply)
996 {
997   /* send HTTP headers */
998   Writefmt(connfd,"HTTP/1.1 200 OK\r\n");
999   Writefmt(connfd,"Transfer-Encoding: chunked\r\n");
1000   Writefmt(connfd,"Keep-Alive: timeout=300\r\n");
1001   Writefmt(connfd,"Connection: Keep-Alive\r\n");
1002   Writefmt(connfd,"Content-Type: text/html\r\n");
1003   Writefmt(connfd,"\r\n");
1004   
1005   if(!isNull(reply)){
1006     /* length of chunk in hex */
1007     Writefmt(connfd, "%x\r\n", strlen(reply));
1008     /* the chunk content */
1009     Writefmt(connfd, "%s\r\n", reply);
1010   }
1011
1012   /* chunk end */
1013   Writefmt(connfd,"0\r\n");
1014   Writefmt(connfd,"\r\n");
1015 }
1016
1017 /*************************************************************/
1018 /* send NOT FOUND reply to unknown http request              */
1019 /* simulate web server response                              */
1020 /*************************************************************/
1021 void sendHttpNotFound(void)
1022 {
1023   /* send HTTP headers */
1024   Writefmt(connfd,"HTTP/1.1 404\r\n");
1025   Writefmt(connfd,"\r\n");
1026 }
1027
1028
1029
1030 /***************************************************/
1031 /***************************************************/
1032 void GetPeerAddr(int sockfd, char *peerAddr)
1033 {
1034   if(debug>1) err_msg("DEBUG:=>getPeerAddr( )"); 
1035   getPeerAddr(sockfd,peerAddr);
1036   if(debug>1) err_msg("DEBUG:<=getPeerAddr(,%s)",peerAddr);
1037 }
1038
1039
1040 int GetListenPort(void)
1041 {
1042   int ret;
1043
1044   if(debug>1) err_msg("DEBUG:=>getListenPort( )");
1045   ret=getListenPort();
1046   if(debug>1) err_msg("DEBUG:(%d)<=getListenPort( )",ret);
1047
1048   return ret;
1049 }
1050
1051 int WaitClientConnect(char *userid, char *userProperty, char *sessionId, char *clientAddr4, char *clientAddr6, int duration, char *macAddr4, char *macAddr6, int ipStatus, struct clientAddr *pClientAddr, char *language, int port, int pid, int cookieAuth, char *redirectedUrl)
1052 {
1053   int ret;
1054
1055   if(debug>1) err_msg("DEBUG:=>waitClientConnect(%s,%s,%s,%s,%s,%d,%s,%s,%d,%s,%d,%d,%d,%s)",userid,userProperty,sessionId,clientAddr4,clientAddr6,duration,macAddr4,macAddr6,ipStatus,language,port,pid,cookieAuth,redirectedUrl);
1056   ret=waitClientConnect(userid,userProperty,sessionId,clientAddr4,clientAddr6,duration,macAddr4,macAddr6,ipStatus,pClientAddr,language,port,pid,cookieAuth,redirectedUrl);
1057   if(debug>1) err_msg("DEBUG:(%d)<=waitClientConnect( )",ret);
1058
1059   return ret;
1060 }
1061
1062 void SendQuitClient(void)
1063 {
1064   if(debug>1) err_msg("DEBUG:=>sendQuitClient( )");
1065   sendQuitClient();
1066   if(debug>1) err_msg("DEBUG:<=sendQuitClient( )");
1067 }
1068
1069 void SendTerminateReply(void)
1070 {
1071   if(debug>1) err_msg("DEBUG:=>sendTerminateReply( )");
1072   sendTerminateReply();
1073   if(debug>1) err_msg("DEBUG:<=sendTerminateReply( )");
1074 }
1075
1076 void WaitHttpClose(struct clientAddr *pClientAddr, char *userid, char *userProperty, char *macAddr4, char *macAddr6, int ipStatus, char *sessionid, int port)
1077 {
1078   if(debug>1) err_msg("DEBUG:=>waitHttpClose(%p,%s,userProperty,%s,%s,%d,%s,%d)",pClientAddr,userid,macAddr4,macAddr6,ipStatus, sessionid, port);
1079   waitHttpClose(pClientAddr,userid,userProperty,macAddr4,macAddr6,ipStatus, sessionid, port);
1080   if(debug>1) err_msg("DEBUG:<=waitHttpClose( )");
1081
1082 }
1083
1084 void ReadHttpHeaders(void)
1085 {
1086   if(debug>1) err_msg("DEBUG:=>readHttpHeaders( )");
1087   readHttpHeaders();
1088   if(debug>1) err_msg("DEBUG:<=readHttpHeaders( )");
1089 }
1090
1091 void SendReplyToGetHello(void)
1092 {
1093   if(debug>1) err_msg("DEBUG:=>sendReplyToGetHello( )");
1094   sendReplyToGetHello();
1095   if(debug>1) err_msg("DEBUG:<=sendReplyToGetHello( )");
1096 }
1097
1098 void SendHttpKeepPage(char *userid, char *sessionId, char *language, int port, int cookieAuth, char *redirectedUrl)
1099 {
1100   if(debug>1) err_msg("DEBUG:=>sendHttpKeepPage(%s,%s,%s,%d,%d,%s)", userid, sessionId, language, port, cookieAuth, redirectedUrl);
1101   sendHttpKeepPage(userid, sessionId, language, port, cookieAuth, redirectedUrl);
1102   if(debug>1) err_msg("DEBUG:<=sendHttpKeepPage( )");
1103
1104 }
1105
1106 void OnUsageTimeLimitAlarm(int signo){
1107
1108   if(debug>1) err_msg("DEBUG:=>onUsageTimeLimitAlarm()");
1109   onUsageTimeLimitAlarm(signo);
1110   if(debug>1) err_msg("DEBUG:<=onUsageTimeLimitAlarm()");
1111 }
1112
1113 void OnCheckBasicAlarm(int signo){
1114
1115   if(debug>1) err_msg("DEBUG:=>onCheckBasicAlarm()");
1116   onCheckBasicAlarm(signo);
1117   if(debug>1) err_msg("DEBUG:<=onCheckBasicAlarm()");
1118 }
1119
1120 void CheckBasic(void){
1121
1122   if(debug>1) err_msg("DEBUG:=>checkBasic()");
1123   checkBasic();
1124   if(debug>1) err_msg("DEBUG:<=checkBasic()");
1125 }
1126
1127 void OnCheckHttpAlarm(int signo){
1128
1129   if(debug>1) err_msg("DEBUG:=>onCheckHttpAlarm()");
1130   onCheckHttpAlarm(signo);
1131   if(debug>1) err_msg("DEBUG:<=onCheckHttpAlarm()");
1132 }
1133
1134 void CheckHttp(void){
1135
1136   if(debug>1) err_msg("DEBUG:=>checkHttp()");
1137   checkHttp();
1138   if(debug>1) err_msg("DEBUG:<=checkHttp()");
1139 }
1140
1141 void OnReadWaitAlarm(int signo){
1142
1143   if(debug>1) err_msg("DEBUG:=>onReadWaitAlarm()");
1144   onReadWaitAlarm(signo);
1145   if(debug>1) err_msg("DEBUG:<=onReadWaitAlarm()");
1146 }
1147
1148 void OnAjaxWaitAlarm(int signo){
1149
1150   if(debug>1) err_msg("DEBUG:=>onAjaxWaitAlarm()");
1151   onAjaxWaitAlarm(signo);
1152   if(debug>1) err_msg("DEBUG:<=onAjaxWaitAlarm()");
1153 }
1154
1155 int SelectAccept(void){
1156   int ret;
1157
1158   if(debug>1) err_msg("DEBUG:=>selectAccept()");
1159   ret=selectAccept();
1160   if(debug>1) err_msg("DEBUG:(%d)<=selectAccept()",ret);
1161
1162   return ret;
1163 }
1164
1165 void AcceptHttpReConnect(int timeout){
1166   if(debug>1) err_msg("DEBUG:=>acceptHttpReConnect(%d)",timeout);
1167   acceptHttpReConnect(timeout);
1168   if(debug>1) err_msg("DEBUG:<=acceptHttpReConnect()");
1169 }
1170
1171 int MacAddrCheck(int ipStatus, char *clientAddr4, char *clientAddr6, char *macAddr4, char *macAddr6) {
1172   int ret;
1173
1174   if(debug>1) err_msg("DEBUG:=>macAddrCheck(%d,%s,%s,%s,%s)", 
1175                       ipStatus, clientAddr4, clientAddr6, macAddr4, macAddr6);
1176   ret=macAddrCheck(ipStatus, clientAddr4, clientAddr6, macAddr4, macAddr6);
1177   if(debug>1) err_msg("DEBUG:(%d)<=macAddrCheck()",ret);
1178
1179   return ret;
1180 }
1181
1182 int IsRightKey(char *pNowKey, char *sessionId){
1183   int ret;
1184
1185   if(debug>1) err_msg("DEBUG:=>isRightKey(%s,%s)", pNowKey, sessionId);
1186   ret=isRightKey(pNowKey, sessionId);
1187   if(debug>1) err_msg("DEBUG:(%d)<=isRightKey()",ret);
1188
1189   return ret;
1190 }
1191
1192 void SendHttpReply(char *reply){
1193   if(debug>1) err_msg("DEBUG:=>sendHttpRepy(%s)", reply);
1194   sendHttpReply(reply);
1195   if(debug>1) err_msg("DEBUG:<=sendHttpReply()");
1196 }
1197
1198
1199 void SendHttpNotFound(void){
1200   if(debug>1) err_msg("DEBUG:=>sendHttpNotFound()");
1201   sendHttpNotFound();
1202   if(debug>1) err_msg("DEBUG:<=sendHttpNotFound()");
1203 }
1204