/* * File: net.c * * Author: Ulli Horlacher (framstag@rus.uni-stuttgart.de) * * Contrib.: Heiko Schlichting (heiko@fu-berlin.de) * Holger Berger (Holger.Berger@rus.uni-stuttgart.de) * Andreas "Ako" Koppenhöfer (koppenhoefer@belwue.de) * * History: * * 11 Aug 95 Framstag initial version * 10 Sep 95 Framstag some debugging * 15 Nov 95 Framstag improved sock_getline * 21 Dec 95 Framstag simplified sock_getline and getreply * 17 Apr 96 Framstag new error handling in open_connection * 14 May 96 Framstag included and modified send_data() * 21 May 96 Framstag gettimeofday() fix for Solaris-2 * 20 Jun 96 Framstag always use gethostbyname() * 3 Aug 96 Framstag corrected thruput value * 4 Sep 96 Heiko some fixes for IRIX * 24 Sep 96 Heiko added get_domainname() * 19 Nov 96 Framstag fix for broken AIX include-files * 29 Dec 96 Framstag moved get_domainname to reply.c * 19 Jan 97 Framstag added "resuming at ..." output * 25 Jan 97 Framstag send_data can now read from stdin, too * 31 Jan 97 Framstag print final transfer statistic with quiet mode 1 * 24 Feb 97 Framstag sprintf() -> snprintf() * 15 Jun 97 Framstag added file reading test mode * corrected transfer statistics output * 16 Jun 97 Hobel socket tuning * 30 Jun 97 Framstag fixed bug in transfer statistics output * 21 Aug 97 Framstag sendheader can handle better reply code 202 * 9 Dec 97 Framstag added sendcommand() * 15 Dec 97 Framstag new parameter for send_data(): speed * 27 Feb 98 Framstag more verbose transfer statistics * 7 Mar 98 Framstag better detection of non-interactive mode * added -i option for more transfer information * 2 Apr 98 Framstag don't ask for server-reply on QUIT command * 23 Jun 98 Framstag fixed byte counting bug in transfer statistics * 4 Jul 98 Ako set tcp timeout option * 7 Jul 98 Ako fixed fflush(NULL) bug for SunOS 4 * 21 Aug 98 Framstag added maximum thruput option for send_data * * Network routines for the the sendfile client of the sendfile package. * Look at net.h for a list of the functions. * * Copyright © 1995-1998 Ulli Horlacher * This file is covered by the GNU General Public License */ /* #ifdef NEXT typedef unsigned char u_char; typedef unsigned short u_short; typedef unsigned int u_int; typedef unsigned long u_long; typedef long daddr_t; typedef char *caddr_t; typedef long time_t; #define _TIME_T #endif */ #include "config.h" /* various definitions */ #include #include #include #include #include #include #include #include #include #include #include "string.h" /* extended string functions */ #include "message.h" /* information, warning and error messages */ #include "net.h" /* network stuff */ #include "io.h" /* socket read/write */ /* stupid AIX comes with no include files for networking and other stuff */ #if defined(AIX3) || defined(ULTRIX) #include "bsd.h" #endif /* #ifdef SOLARIS2 #ifdef _SVID_GETTOD int gettimeofday(struct timeval *); #else int gettimeofday(struct timeval *, void *); #endif #endif */ #if defined(SOLARIS2) #ifndef fileno int fileno(FILE *); #endif #endif /* #ifdef IRIX u_short htons(u_short hostshort); #endif */ extern int client; /* client or server flag */ extern int verbose; /* flag for verbose mode */ extern char *prg; /* name of the game */ /* * open_connection - open socket and connect to client * * INPUT: adr - ip address or name of server to connect to * port - port number to connect to * * RETURN: socket file descriptor * -1 if socket creation failed * -2 if connection failed * -3 if unknown host * * this function is derived from example code from * "Unix Networking Programming" by W. R. Stevens */ int open_connection(char *adr, int port) { int sockfd, /* socket file descriptor */ num=1; /* flag for numeric ip address */ struct sockaddr_in serv_addr; /* internet socket */ struct in_addr hostaddr; struct hostent *hostp; /* host entity */ char *cp, /* character pointer */ hostip[17], /* server host ip address */ hostname[MAXLEN]; /* server host name */ /* split off port from hostname */ strcpy(hostname,adr); if ((cp=strchr(hostname,':'))) *cp=0; /* open socket */ sockfd=socket(AF_INET,SOCK_STREAM,0); if (sockfd<0) return(-1); #ifdef DEBUG message(prg,'I',"socket ok"); #endif /* initialisize serv_addr */ memset((char *) &serv_addr, 0, sizeof(serv_addr)); /* numeric oder symbolic ip address? */ for (cp=hostname; *cp>0; cp++) { if (*cp>'@') { num=0; break; } } /* quick hack: gethostbyname() does also work with numeric addresses */ num=0; /* look for server host address */ if (num) { hostaddr.s_addr=inet_addr(hostname); hostp=gethostbyaddr((char *)&hostaddr,sizeof(hostaddr),AF_INET); } else hostp=gethostbyname(hostname); if (hostp==NULL) return(-3); /* convert binary structure to ASCII hostip */ strcpy(hostip,inet_ntoa(*(struct in_addr *) *hostp->h_addr_list)); #ifdef DEBUG printf("host: %s\n",hostip); #endif /* fill out server address descriptor */ serv_addr.sin_family =AF_INET; serv_addr.sin_addr.s_addr=inet_addr(hostip); serv_addr.sin_port =htons(port); /* befor connecting, let's do some tuning :-) hobel */ { int flag; flag=1; #ifdef SO_KEEPALIVE if(setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, (void *)&flag, sizeof(flag))<0) message(prg,'W',"could not configure socket"); #endif #ifdef TCP_NODELAY flag=1; if(setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, (void *)&flag, sizeof(flag))<0) message(prg,'W',"could not configure socket"); #endif #ifdef TCP_BUFFER flag=TCP_BUFFER; if(setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, (void *)&flag, sizeof(flag))<0) message(prg,'W',"could not configure socket"); flag=TCP_BUFFER; if(setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, (void *)&flag, sizeof(flag))<0) message(prg,'W',"could not configure socket"); #endif #ifdef TCP_RFC1323 flag=1; if(setsockopt(sockfd, IPPROTO_TCP,TCP_RFC1323, (void *)&flag, sizeof(flag))<0) message(prg,'W',"could not configure socket"); #endif #ifdef TCP_WINSHIFT flag=1; /* this gives the power of two to be shifted */ if(setsockopt(sockfd, IPPROTO_TCP, TCP_WINSHIFT, (void *)&flag, sizeof(flag))<0) message(prg,'W',"could not configure socket"); #endif } /* connect to server */ if (connect(sockfd,(struct sockaddr *)&serv_addr,sizeof(serv_addr)) < 0) return(-2); #ifdef DEBUG message(prg,'I',"connect ok"); #endif return(sockfd); } /* * sock_getline - get a (command) line from the network socket * * INPUT: fd - socket file descriptor * line - empty string * * OUTPUT: line - read string * * RETURN: number of read bytes, -1 on error * * this function is derived from example code from * "Unix Networking Programming" by W. R. Stevens */ int sock_getline(int fd, char *line) { int n, rc; unsigned char c; char tmp[MAXLEN]; *line=0; for (n=0; n0 && line[n-1]=='\r') line[--n]=0; /* on verbose mode show the whole line */ if (verbose) printf("%s\n",line); return(n); } /* * sock_putline - send a line to the network socket * * INPUT: fd - socket file descriptor * line - string to send * * RETURN: number of send bytes */ int sock_putline(int fd, const char *line) { int n; /* number of send bytes */ char cmd[MAXLEN]; /* command line to send */ /* prepare string */ strcpy(cmd,line); strcat(cmd,"\r\n"); /* on verbose mode show what goes up */ if (verbose) printf("-> %s\n",line); /* and up and away :-) */ if (fd) n=writen(fd,cmd,strlen(cmd)); else /* test mode, no real sending */ n=strlen(cmd); return(n); } /* * getreply - get the reply on a command from the server * * INPUT: fd - socket file descriptor * * RETURN: the reply line string */ char *getreply(int fd) { int len; /* reply message length */ char msg[MAXLEN]; /* intermediate information/error message */ static char reply[MAXLEN]; /* reply string from server */ do { if (fd) { /* get the next reply line */ len=sock_getline(fd,reply); /* link failure? */ if (len<0) { errno=0; strcpy(msg,"server has closed the connection"); if (*reply) snprintf(MAXS(msg),"%s, last data: \"%s\"",msg,reply); if (client) { errno=0; message("",'F',msg); } return(""); } /* reply message too short? */ if (len<4) { errno=0; snprintf(MAXS(msg),"corrupt reply: \"%s\"",reply); if (client) { errno=0; message(prg,'F',msg); } return(""); } } else /* test mode without network connection */ strcpy(reply,"222 test mode"); } while (reply[3]=='-'); /* quit if there was a fatal server error */ if (reply[0]=='4') { errno=0; snprintf(MAXS(msg),"server error: %s",&reply[4]); if (client) { errno=0; message(prg,'F',msg); } return(""); } return(reply); } /* * sendcommand - send a command line to the network socket and get the answer * * INPUT: fd - socket file descriptor * command - string to send * * OUTPUT: answer - answer string * * RETURN: answer string * * If answer is the NULL-pointer, the answer will bis discared. * See also sendheader() * Hack: because some SAFT-servers hang on the QUIT command, we don't ask for * the reply in this case. */ char *sendcommand(int fd, const char *command, char *answer) { if (answer) *answer=0; sock_putline(fd,command); if (answer) strcpy(answer,getreply(fd)); else if (!str_eq(command,"QUIT")) getreply(fd); return(answer); } /* * sendheader - send a headerline and check the reply code * * INPUT: fd - socket file descriptor * line - header line * * RETURN: 0 on sucess, -1 on server fatal error, 1 on header ignored * * Return value -1 never occurs because the program will terminate before * See also sendcommand() */ int sendheader(int fd, char *line) { char msg[MAXLEN], /* intermediate information/error message */ *reply; /* reply string from server */ /* on test mode return */ if (!fd) return(0); /* send the header line */ sock_putline(fd,line); /* server reply ok? */ reply=getreply(fd); if (str_beq(reply,"200")) return(0); if (str_beq(reply,"202")) return(1); errno=0; snprintf(MAXS(msg),"server error: %s",&reply[4]); message(prg,'F',msg); return(-1); } /* * send_data - send file data * * INPUT: sockfd - socket file descriptor * size - bytes to send * file - file to send ("" if read from stdin) * tinfo - additional transfer information * iso_name - name of the original file * type - file type string * mtp - maximum thruput * * OUTPUT: ttime - transfer time in ms * * RETURN: 0 if ok, 1 if already transfered, -1 if transfer failed */ int send_data(int sockfd, unsigned long size, const char *file, const char *tinfo, const char *iso_name, const char *type, float mtp, float *ttime) { int n, /* simple loop count */ nblocks, /* number of packets blocks */ bn, /* block number to read */ percent, /* what percentage of file has been transmitted */ ffd; /* file to send file descriptor */ unsigned long msec, /* milliseconds of transfer */ bytes, /* bytes which has been sent */ offset; /* bytes already sent */ float thruput; /* network thruput */ char packet[OVERSIZE], /* data packet to send */ tmp[MAXLEN], /* temporary string */ fname[MAXLEN], /* file name and compress info */ *reply; /* reply string */ time_t sec0,sec1,sec2; /* unix time */ struct timeval tv1,tv2; #if !defined(SOLARIS2) && !defined(IRIX) struct timezone tz; #endif extern int quiet, /* quiet mode flag */ packet_size; /* size of a packet in bytes */ msec=0; offset=0; /* fallback */ if (packet_size<1) packet_size=512; if (ttime) *ttime=0; strcpy(fname,iso_name); if (*type) { strcat(fname," ("); strcat(fname,type); strcat(fname,")"); } if (!quiet) printf("sending... \r"); fflush(stdout); /* real sending and no local testing? */ if (sockfd) { /* real file to send? ==> ask for resend */ if (*file) { sock_putline(sockfd,"RESEND"); reply=getreply(sockfd); /* correct answer? */ if (!str_beq(reply,"500 ") && !str_beq(reply,"502 ")) { /* error occured? */ if (!str_beq(reply,"230 ")) { if (quiet<3) { errno=0; snprintf(MAXS(tmp),"server error: %s",&reply[4]); message("",'F',tmp); } return(-1); } sscanf(&reply[4],"%ld",&offset); } } /* prepare sending of data */ sock_putline(sockfd,"DATA"); reply=getreply(sockfd); /* file already transmitted? */ if (str_beq(reply,"531 ")) { snprintf(MAXS(tmp), "file %s has been already transmitted - ignored.",iso_name); if (quiet<2) message("",'W',tmp); return(1); } /* server reply ok? */ if (!str_beq(reply,"302 ")) { if (quiet<3) { snprintf(MAXS(tmp),"corrupt server reply: %s",&reply[4]); errno=0; message("",'F',tmp); } return(-1); } } /* open file */ if (*file) { ffd=open(file,O_RDONLY,0); if (ffd<0 || lseek(ffd,offset,SEEK_SET)<0) { if (quiet<3) { snprintf(MAXS(tmp),"error reading %s",iso_name); message("",'E',tmp); } return(-1); } } else ffd=fileno(stdin); /* resend active? */ if (offset) { snprintf(MAXS(tmp),"resuming %s at byte %ld",iso_name,offset); if (quiet<2) message("",'I',tmp); } if (quiet==1) { snprintf(MAXS(tmp),"begin transfer of %s with %lu bytes",fname,size); message("",'I',tmp); } /* get time normal */ #if defined(SOLARIS2) || defined(IRIX) #ifdef _SVID_GETTOD gettimeofday(&tv1); #else gettimeofday(&tv1,NULL); #endif #else gettimeofday(&tv1,&tz); #endif sec1=0; sec0=time(0); /* send the file data in packets */ bytes=0; size=size-offset; nblocks=size/packet_size; for (bn=1; bn<=nblocks; bn++) { if (readn(ffd,packet,packet_size)sec1) { fprintf(stderr,"%s%s: %3d%% (%ld of %ld kB)\r", tinfo,fname,percent, (bytes+offset-1)/1024+1,(size+offset-1)/1024+1); fflush(stderr); sec1=sec2; } } /* limit maximum thruput */ if (mtp) while (bytes/(time(0)-sec0+0.001)>mtp*1024) sleep(1); } /* send the last bytes */ if ((n=size-nblocks*packet_size) > 0) { if (readn(ffd,packet,n)9999) snprintf(MAXS(tmp), "transfer of %s completed: %.1f kB/s",fname,thruput/1024); else snprintf(MAXS(tmp), "transfer of %s completed: %d byte/s",fname,(int)thruput); message("",'I',tmp); } else { fprintf(stderr,"%s%s: 100%% (",tinfo,fname); if (bytes>9999) fprintf(stderr,"%ld kB, ",bytes/1024); else fprintf(stderr,"%ld byte, ",bytes); if (thruput>9999) fprintf(stderr,"%.1f kB/s) \n",thruput/1024); else fprintf(stderr,"%d byte/s) \n",(int)thruput); fflush(stderr); } } if (ttime) *ttime=(float)msec; /* transfer ok? */ if (sockfd && !str_beq(getreply(sockfd),"201 ")) { if (quiet<3) { snprintf(MAXS(tmp),"transfer failed for %s",iso_name); errno=0; message("",'E',tmp); } return(-1); } return(0); }