/*********************************************************************\ * Copyright (c) 2004 by Radim Kolar * * Copyright (c) 1991 by Wen-King Su (wen-king@vlsi.cs.caltech.edu) * * * * You may copy or modify this file in any manner you wish, provided * * that this notice is always included, and that you hold the author * * harmless for any loss or damage resulting from the installation or * * use of this software. * \*********************************************************************/ #include "tweak.h" #include "client_def.h" #include "c_extern.h" #include "co_extern.h" #include #ifdef HAVE_UNISTD_H #include #endif static int myfd; static struct sockaddr_in server_addr; static unsigned short myseq = 0; static unsigned short key; int client_trace = 0; int client_intr_state = 0; unsigned long target_delay = DEFAULT_DELAY; /* expected max delay from server on good connection */ unsigned long target_maxdelay = DEFAULT_MAXDELAY; /* max resend timer */ unsigned long busy_delay = DEFAULT_DELAY; /* busy retransmit timer */ unsigned long idle_delay = DEFAULT_DELAY; /* idle retransmit timer */ unsigned long udp_sent_time; unsigned long stat_resends, stat_iresends, stat_dupes, stat_bad, stat_ok; UBUF *client_interact (unsigned char cmd, unsigned long pos, unsigned int l1, unsigned const char * p1, unsigned int l2, unsigned const char * p2) { struct sockaddr_in from; UBUF sbuf; static UBUF rbuf; unsigned char *s, *t, *d, seq0, seq1; unsigned u, n, sum, mlen, rlen; fd_set mask; int retval, retry_send, retry_recv; socklen_t bytes; unsigned long w_delay; unsigned long total_delay; struct timeval start[8],stop; FD_ZERO(&mask); sbuf.cmd = cmd; #ifdef DEBUG printf("sbuf.cmd = %u\n",sbuf.cmd); #endif BB_WRITE2(sbuf.bb_len,l1); BB_WRITE4(sbuf.bb_pos,pos); client_intr_state = 1; total_delay = 0; w_delay = 0; for(u = l1, d = (unsigned char *) sbuf.buf; u--; *d++ = *p1++); for(u = l2; u--; *d++ = *p2++); mlen = d - (unsigned char *) &sbuf; key = client_get_key(); for(retry_send = 0; ; retry_send++) { total_delay += w_delay; BB_WRITE2(sbuf.bb_key,key); sbuf.bb_seq[0] = seq0 = (myseq >> 8) & 0xff; sbuf.bb_seq[1] = seq1 = (myseq & 0xf8) | (retry_send & 0x0007); sbuf.sum = 0; for(t = (unsigned char *) &sbuf, sum = n = mlen; n--; sum += *t++); sbuf.sum = sum + (sum >> 8); switch(retry_send) { /* adaptive retry delay adjustments */ case 0: if(target_delay>=busy_delay) w_delay=target_delay; else w_delay=busy_delay+DEFAULT_DELAY; /* classic FSP retry code. Not suitable for CZFREE.NET busy_delay = (target_delay+ (busy_delay<<3)-busy_delay)>>3; w_delay = busy_delay; */ break; case 1: busy_delay = busy_delay * 3 / 2; w_delay = busy_delay; idle_delay = busy_delay; if(client_trace) write(2,"R",1); stat_resends++; break; default: #ifdef CLIENT_TIMEOUT if (total_delay/1000 >= env_timeout ) { fprintf(stderr, "\rRemote server not responding.\n"); exit(EX_UNAVAILABLE); } #endif idle_delay = idle_delay * 4 / 3; if (idle_delay > target_maxdelay) idle_delay = target_maxdelay; w_delay = idle_delay; if(client_trace) write(2,"I",1); stat_iresends++; break; } if(sendto(myfd,(const char*)&sbuf,mlen,0,(struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) { switch(errno) { case ENOBUFS: case EHOSTUNREACH: case ECONNREFUSED: case EHOSTDOWN: case ENETDOWN: case EPIPE: /* try to resend packet */ continue; default: perror("sendto"); exit(EX_IOERR); } } /* Check if w_delay is within limits */ if(w_delay < MIN_DELAY) w_delay=MIN_DELAY; else if (w_delay > target_maxdelay) w_delay = target_maxdelay; #ifdef DEBUG printf("Waiting %lu ms for server response.\n",w_delay); #endif udp_sent_time = time((time_t *) 0); gettimeofday(&start[retry_send & 0x7],NULL); FD_SET(myfd,&mask); for(retry_recv = 0; ; retry_recv++) { retval = _x_select(&mask, w_delay); if((retval == -1) && (errno == EINTR)) continue; if(retval == 1) { /* an incoming message is waiting */ bytes = sizeof(from); if((bytes = recvfrom(myfd,(char*)&rbuf,sizeof(rbuf),0, (struct sockaddr *)&from, &bytes)) < UBUF_HSIZE) { /* too enough bytes for header */ if (client_trace) write(2,"H",1); stat_bad++; continue; } rlen = BB_READ2(rbuf.bb_len); if( (rlen+UBUF_HSIZE) > bytes) { /* truncated. */ if (client_trace) write(2,"T",1); stat_bad++; continue; } s = (unsigned char *) &rbuf; d = s + bytes; u = rbuf.sum; rbuf.sum = 0; for(t = s, sum = 0; t < d; sum += *t++); sum = (sum + (sum >> 8)) & 0xff; if(sum != u) { /* wrong check sum */ if (client_trace) write(2,"C",1); stat_bad++; continue; } if( (rbuf.bb_seq[0] ^ seq0) || ((rbuf.bb_seq[1] ^ seq1)&0xf8)) { /* wrong seq # */ if (client_trace) write(2,"S",1); stat_dupes++; continue; } myseq = (myseq + 0x0008) & 0xfff8; /* seq for next request */ key = BB_READ2(rbuf.bb_key); /* key for next request */ /* calculate real busy delay */ gettimeofday(&stop,NULL); busy_delay = 1000*(stop.tv_sec-start[rbuf.bb_seq[1] & 0x7].tv_sec); busy_delay += (stop.tv_usec-start[rbuf.bb_seq[1] & 0x7].tv_usec)/1000; #ifdef DEBUG printf("Server reply RTT was %lu ms.\n",busy_delay); #endif client_set_key(key); stat_ok++; if(client_intr_state == 2) { if(!key_persists) client_done(); exit(EX_TEMPFAIL); } #ifdef DEBUG printf("rbuf.cmd = %u\n",rbuf.cmd); #endif return(&rbuf); } else break; /* go back to re-transmit buffer again */ } } } static RETSIGTYPE client_intr (int signum) { switch(client_intr_state) { case 0: exit(EX_TEMPFAIL); case 1: client_intr_state = 2; break; case 2: exit(EX_TEMPFAIL); } #ifndef RELIABLE_SIGNALS signal(SIGINT,client_intr); #endif } void init_client (const char * host, unsigned short port, unsigned short myport) { busy_delay = idle_delay = target_delay; stat_resends = stat_iresends = stat_dupes = stat_bad = stat_ok; #ifdef HAVE_SRANDOMDEV srandomdev(); #endif myseq = random() & 0xfff8; if((myfd = _x_udp(env_listen_on,&myport)) == -1) { perror("socket open"); exit(EX_OSERR); } if(_x_adr(host,port,&server_addr) == -1) { perror("server addr"); exit(EX_OSERR); } client_init_key(server_addr.sin_addr.s_addr,port,getpid()); signal(SIGINT,client_intr); } void client_finish(void) { env_timeout=10; (void) client_interact(CC_BYE, 0L, 0, (unsigned char *)NULLP, 0, (unsigned char *)NULLP); client_destroy_key(); } int client_done (void) { if(!key_persists) client_finish(); return(0); }