diff --git a/ChangeLog b/ChangeLog index 6c7d9de..7bb08f7 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,27 @@ +Version NEXT + PROTOCOL updated. Made rules about breaking directory header on + packet boundary more clear. Previous text was from fspd code comment + and was bad. + fixed tests/mklargefile.py + log uploads to wuftpd logfile also + PROTOCOL file updated. Added required commands section. + PROTOCOL file updated. Added recommended delay management section. + default delay in clients adjusted from 1.5 to 1.34 sec as recommended + in PROTOCOL + support for MIN_DELAY, MAX_DELAY and DEFAULT_DELAY in source + path parsing moved to separate file server/path.c + new test tool parsecheck + reworked parse path: separate error messages for high bit chars, + control chars + do not segfault when parsing password protected files. Looks like + nobody used password protected files for a long time. This means + that in future version of FSP I can change protocol definition + without worry about breaking anything + path with trailing / or starting / is now parsed correctly + pathes with dot after slash are now detected at parse level (side + effect of prev. fix) + tests moved from server/ into tests/ directory + Version 2.8.1b17 -- 17 Nov 2003 Allow filenames with spaces inside check for fork() and setsid() in configure.ac diff --git a/TODO b/TODO index 5ef91ea..75c947a 100644 --- a/TODO +++ b/TODO @@ -59,7 +59,7 @@ bind-ip-address for server (and client via FSP_LOCAL_IP) report number of clients connected, size of hostable in new command CC_INFO command special defence against rapid/double fire clients -new common log format +common log format - replace custom fspd log :midle:may not be in 2.8.2 Native Supports for symbolic links (needed for mirroring Debian) @@ -80,6 +80,7 @@ PERFORMANCE: host hashtable shrinking sometimes stat cache pro FSP_STAT a ostatni background time() alarm() caller +Current performance 1925648b/s MAN: update FAQ - urgent!! @@ -93,4 +94,4 @@ FSP plugin do Netscape/MSIE FSP library for Java Transfer HTTP over fsp transport FSP backend for APT -FSP support to lftp +FSP support to lftp,wget,curl diff --git a/client/lib.c b/client/lib.c index dfb4f24..2d38bfd 100644 --- a/client/lib.c +++ b/client/lib.c @@ -26,9 +26,9 @@ static unsigned short key; int client_trace = 0; int client_intr_state = 0; -unsigned long target_delay = MIN_DELAY; /* expected max delay */ -unsigned long busy_delay = MIN_DELAY; /* busy retransmit timer */ -unsigned long idle_delay = MIN_DELAY; /* idle retransmit timer */ +unsigned long target_delay = DEFAULT_DELAY; /* expected max delay */ +unsigned long busy_delay = DEFAULT_DELAY; /* busy retransmit timer */ +unsigned long idle_delay = DEFAULT_DELAY; /* idle retransmit timer */ unsigned long udp_sent_time; UBUF *client_interact PROTO6(unsigned char, cmd, unsigned long, pos, @@ -79,7 +79,8 @@ UBUF *client_interact PROTO6(unsigned char, cmd, unsigned long, pos, case 1: busy_delay = busy_delay * 3 / 2; w_delay = busy_delay; - if(client_trace) write(2,"R",1); break; + if(client_trace) write(2,"R",1); + break; default: #ifdef CLIENT_TIMEOUT diff --git a/client/util.c b/client/util.c index 3c283f5..1fbc88d 100644 --- a/client/util.c +++ b/client/util.c @@ -448,6 +448,7 @@ static void util_get_env PROTO0((void)) if( (p = getenv("FSP_DELAY")) ) target_delay = atol(p); if(target_delay < MIN_DELAY) target_delay = MIN_DELAY; + if(target_delay > MAX_DELAY) target_delay = MAX_DELAY; if(!(env_local_dir = getenv("FSP_LOCAL_DIR"))) env_local_dir="."; diff --git a/configure.ac b/configure.ac index a933afa..79f9ed9 100644 --- a/configure.ac +++ b/configure.ac @@ -1,6 +1,6 @@ dnl Process this file with autoconf to produce a working configure script. dnl tested with autoconf 2.57 -AC_INIT(fsp,2.8.1b17,hsn@cybermail.net) +AC_INIT(fsp,2.8.1b17+cvs,hsn@cybermail.net) AC_CONFIG_SRCDIR(server/main.c) AM_INIT_AUTOMAKE([dist-bzip2]) AM_MAINTAINER_MODE @@ -53,10 +53,10 @@ then -Wimplicit -Wsequence-point -Wreturn-type \ -Wfloat-equal $addopts \ -Wno-system-headers -Wredundant-decls \ - -Wmissing-noreturn -Wconversion -pedantic \ + -Wmissing-noreturn -pedantic \ -Wlong-long -Wundef -Winline \ -Wno-unused-parameter -# -Wunreachable-code +# -Wunreachable-code -Wconversion do if $CC $i $ac_cv_prog_gcc_flags -c configure-dummy.c >/dev/null 2>&1 then ac_cv_prog_gcc_flags="$ac_cv_prog_gcc_flags $i" diff --git a/doc/PROTOCOL b/doc/PROTOCOL index e5aaa1a..0661371 100644 --- a/doc/PROTOCOL +++ b/doc/PROTOCOL @@ -1,8 +1,8 @@ - File Service Protocol (FSP) - definition + File Service Protocol version 2 + specification - Document version 0.8 - Last updated 29 Sep 2003 + Document version 0.9 + Last updated 18 Nov 2003 Also known as File Slurping Protocol, @@ -72,7 +72,10 @@ by server. TIMEOUTS Resends: Server will accept resent message from client with old KEY after 3 seconds. -Client MUST wait at least 1 second before resending a message. +Client MUST wait at least 1 second before resending a message. +It is recommended to use initial delay of 1.34 second and after each +unsuccessfull resend multiply delay time by 1.5. Maximum recommended delay +time is 300 seconds. Session: Server will accept message with bad key after 60 seconds. Clients should sent CC_BYE at end of their session, CC_BYE terminates a session. @@ -82,7 +85,7 @@ SEQUENCE Similarly, the server's message to client contains a SEQUENCE value that is the same as the SEQUENCE value of the previous message from the client. Client can choose any SEQUENCE number and can use it for detection of lost -packets (increase sequence number on retry). +packets (increase sequence number on message resend). DATA_LENGTH Size of DATA field in packet. Packet can also contain XTRA DATA field but @@ -96,6 +99,16 @@ data. FSP COMMANDS ============ +REQUIRED COMMANDS +FSP File servers MUST supports following commands: +- sending error messages back to client with CC_ERR +- directory listings CC_GET_DIR +- file transfer CC_GET_FILE +- file status CC_STAT +- information about directory flags CC_GET_PRO +- terminate session CC_BYE + + CC_VERSION 0x10 - Get server version string and setup request @@ -157,7 +170,7 @@ boundary, then two possible things will happen: 1) if the HEADER fits between this entry and the 1k boundary, a complete header will be filled in with a 'type' set to RDTYPE_SKIP. - And then enough bytes to pad to 1k boundary. + Repeat this step as neceseary until no HEADER fits. 2) if the HEADER does not fit, then simply pad to the 1k boundary. This will make sure that messages carrying directory information carry only diff --git a/include/common_def.h b/include/common_def.h index e20082d..cdc325c 100644 --- a/include/common_def.h +++ b/include/common_def.h @@ -167,6 +167,8 @@ typedef struct RDIRENT { unsigned char bb_time[4]; #define NULLP ((char *) 0) -#define MIN_DELAY 1500L +#define MIN_DELAY 1000L +#define DEFAULT_DELAY 1340L +#define MAX_DELAY 300000L #endif /* _FSP_COMMON_DEF_H_ */ diff --git a/include/s_extern.h b/include/s_extern.h index f54d85d..1219b8f 100644 --- a/include/s_extern.h +++ b/include/s_extern.h @@ -25,6 +25,7 @@ int init_caches PROTO0((void)); void shutdown_caches PROTO0((void)); void stat_caches PROTO1(FILE *,fp); const char *validate_path PROTO0((char *, unsigned, PPATH *,DIRINFO **, int)); +const char *parse_path PROTO3(char *, fullp, unsigned int, len, PPATH *, pp); const char *server_get_dir PROTO0((DIRLISTING **,const DIRINFO *)); const char *server_del_file PROTO0((PPATH *, DIRINFO *)); const char *server_del_dir PROTO2(PPATH *, pp, DIRINFO *,di); diff --git a/server/Makefile.am b/server/Makefile.am index c64627c..89ffe1e 100644 --- a/server/Makefile.am +++ b/server/Makefile.am @@ -1,15 +1,10 @@ ## Process this file with automake to produce Makefile.in bin_PROGRAMS=fspd -if MAINTAINER_MODE -noinst_PROGRAMS=cachecheck -endif fspd_SOURCES=file.c host.c main.c conf.c filecache.c server.c fifocache.c \ - log.c iprange.c acl.c + log.c iprange.c acl.c path.c fspd_CFLAGS=-DSYSCONFDIR="\"@sysconfdir@\"" $(AM_CFLAGS) fspd_LDADD=-L../common -lcommon -cachecheck_SOURCES=fifocache.c cachecheck.c - noinst_HEADERS=fifocache.h diff --git a/server/file.c b/server/file.c index 05cefb9..6ff33ce 100644 --- a/server/file.c +++ b/server/file.c @@ -227,72 +227,6 @@ void shutdown_caches PROTO0((void)) f_cache_destroy(fpcache); } -/***************************************************************************** - * Routine to parse a path string given by the client. - * Will replace null string by ".". - * In case of error, returns the error string. - * - * The PPATH structure is filled in by the function check_path when given a - * path string. The elements are filled in as such: - * - * fullp pointer to a string containing the full path name - * f_ptr pointer to begining of the last component of the path - * f_len length of the last component of the path - * d_ptr pointer to begining of the directory part of path - * d_len length of the directory part of the path - * passwd ptr to password - * - * fullp is a null-terminated full path string. - * f_ptr is always a null-terminated sub-string of fullp. - * d_ptr is generally not null-terminated. - *****************************************************************************/ - -static const char *parse_path PROTO3(char *, fullp, unsigned int, len, PPATH *, pp) -{ - char *s; - int state; - - if(len < 1) return("Path must have non-zero length"); - if(fullp[len-1]) return("Path not null terminated"); - - pp->d_ptr = "."; pp->d_len = 1; /* initial dir part ---> root */ - pp->passwd = NULL; /* default, no password */ - - if(len == 1 && fullp[0] == 0) { /* null path --> root */ - pp->fullp = pp->f_ptr = "."; - pp->f_len = 1; - return(NULLP); - } - - for(s = pp->fullp = pp->f_ptr = fullp, state = 0; *s; s++) { - if(*s == '\n') { - pp->passwd = s+1; - *s = '\0'; - if(dbug) fprintf(stderr,"parse_path: found password field %s\n", s+1); - } - else if(*s < ' ' || *s >= '~') return("Path contains illegal chars"); - - switch(*s) { - case '\\': - case '.': - if(state==0) return("Path can't begin with '.' or '\\'"); - break; - case '/': - if(state!=0) - { - pp->d_ptr=fullp; - pp->d_len=s-fullp; - pp->f_ptr=s+1; - } - default: - state = 1; - break; - } - } - - pp->f_len = s - pp->f_ptr; - return(NULLP); -} /***************************************************************************** diff --git a/server/path.c b/server/path.c new file mode 100644 index 0000000..8bee188 --- /dev/null +++ b/server/path.c @@ -0,0 +1,119 @@ + /*********************************************************************\ + * Copyright (c) 2003 by Radim Kolar (hsn@cybermail.net) * + * 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 "server_def.h" +#include "s_extern.h" + +/***************************************************************************** + * Routine to parse a path string given by the client. + * input: fullp - pointer to path for parsing + * len - length of the path for parsing + * Will replace null string by ".". + * In case of error, returns the error string. + * + * The PPATH structure is filled in by the function check_path when given a + * path string. The elements are filled in as such: + * + * fullp pointer to a string containing the full path name + * f_ptr pointer to begining of the last component of the path + * f_len length of the last component of the path + * d_ptr pointer to begining of the directory part of path + * d_len length of the directory part of the path + * passwd ptr to password + * + * fullp is a null-terminated full path string. + * f_ptr is always a null-terminated sub-string of fullp. + * d_ptr is generally not null-terminated. + *****************************************************************************/ + +const char *parse_path PROTO3(char *, fullp, unsigned int, len, PPATH *, pp) +{ + char *s; + int state; + + if(len < 1) return("Path must have non-zero length"); + if(fullp[len-1]) return("Path not null terminated"); + + pp->passwd = NULL; /* default, no password */ + pp->d_len = 0; + + if(len == 1 && fullp[0] == 0) + { + /* null path --> root */ + pp->fullp = pp->f_ptr = pp->d_ptr = "."; + pp->f_len = pp->d_len = 1; + return(NULLP); + } + + for(s = pp->fullp = pp->f_ptr = pp->d_ptr = fullp, state = 0; *s; s++) + { + if(*s == '\n') + { + pp->passwd = s+1; + *s = '\0'; + if(dbug) fprintf(stderr,"parse_path: found password field %s\n", s+1); + break; + } + else + if(*s < ' ') return("Path contains control chars"); + else + if(*s >= '~') return("Path contains high chars"); + + switch(*s) + { + case '\\': + case '.': + if(state==0) return("Path can't begin with '.' or '\\'"); + if(state==2) return("Files can't begin with '.'"); + default: + state = 1; + break; + case '/': + pp->f_ptr=s+1; + switch(state) + { + case 0: + /* state 0: front slashes */ + pp->fullp=s+1; + pp->d_ptr=s+1; + pp->d_len=0; + break; + case 1: + /* state 1: slash after nonslash */ + pp->d_len= s-pp->fullp; + state=2; + break; + case 2: + break; + } + } + } + + /* pp->d_len = pp->f_ptr - pp->fullp; */ + + /* turn empty directory into "." */ + if(pp->d_len == 0) + { + pp->d_ptr = "."; + pp->d_len = 1; + } + + pp->f_len = s - pp->f_ptr; + + /* turn empty file into "." */ + if(pp->f_len == 0) + { + pp->f_ptr = "."; + pp->f_len = 1; + } + + return(NULLP); +} diff --git a/server/server.c b/server/server.c index f28748f..eb09aa5 100644 --- a/server/server.c +++ b/server/server.c @@ -701,6 +701,8 @@ static void server_process_packet PROTO5(unsigned, bytes, UBUF *, ub, int, old, return; } ACTIONOK(L_INSTALL); + FSP_STAT(pp.fullp,&sd); /* log filesizes */ + xferlog('i',pp.fullp,sd.st_size,inetstr); } server_reply(from,ub,0,0); return; diff --git a/tests/.cvsignore b/tests/.cvsignore index a548a86..c67857c 100644 --- a/tests/.cvsignore +++ b/tests/.cvsignore @@ -2,3 +2,5 @@ Makefile Makefile.in mklargefile +cachecheck +parsecheck diff --git a/tests/Makefile.am b/tests/Makefile.am index 59dff13..2acafbf 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -1,2 +1,7 @@ -noinst_PROGRAMS=mklargefile +noinst_PROGRAMS=mklargefile cachecheck parsecheck + extra_DIST=mklargefile.py + +cachecheck_SOURCES=../server/fifocache.c cachecheck.c +parsecheck_SOURCES=parsecheck.c ../server/path.c + diff --git a/server/cachecheck.c b/tests/cachecheck.c similarity index 98% rename from server/cachecheck.c rename to tests/cachecheck.c index 1525031..aa5eda4 100644 --- a/server/cachecheck.c +++ b/tests/cachecheck.c @@ -7,7 +7,7 @@ #include #include #include "tweak.h" -#include "fifocache.h" +#include "../server/fifocache.h" static int intcompare(const int *i1,const int *i2) { diff --git a/tests/mklargefile.py b/tests/mklargefile.py index 6261c65..11ea7ed 100644 --- a/tests/mklargefile.py +++ b/tests/mklargefile.py @@ -7,6 +7,6 @@ if __name__ == '__main__': else: GB=1024*1024*1024 f=open(sys.argv[1],'w') - f.seek(long(sys.argv[2])*GB) + f.seek(long(float(sys.argv[2])*GB)) f.write('!') f.close(); diff --git a/tests/parsecheck.c b/tests/parsecheck.c new file mode 100644 index 0000000..3326928 --- /dev/null +++ b/tests/parsecheck.c @@ -0,0 +1,62 @@ +#include "tweak.h" +#include "server_def.h" +#include "s_extern.h" +#ifdef STDC_HEADERS +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#endif +#include "my-string.h" + +int dbug=0; + +const char *testcases[]={ "", ".","filename","/filename","//filename","//dirname/filename","//dirname//filename","dirname//dir3name//","filename\npasswd", + "file/.dir","directory.ext/filename.", + NULL}; + +static void print_path(PPATH *pp) +{ + printf("fullpath: %s ",pp->fullp); + if(strcmp(pp->fullp,pp->d_ptr)) + { + printf("d_ptr: %s (%d) ",pp->d_ptr,pp->d_len); + } else + printf("(%d) ",pp->d_len); + + printf("f_ptr: %s (%d) ",pp->f_ptr,pp->f_len); + if(pp->passwd) + printf("passwd: %s",pp->passwd); +} + +static void runtestcase(void) +{ + int i=0; + PPATH pp; + const char *err; + const char *test; + + for(;testcases[i];i++) + { + test=strdup(testcases[i]); + err=parse_path(test,strlen(test)+1,&pp); + printf("parsing: '%s'",test); + if(err) + { + printf(" err: %s\n",err); + free(test); + continue; + } else + printf(" okay.\n"); + printf(" "); + print_path(&pp); + printf("\n"); + free(test); + } +} + +int main(int argc,const char *argv[]) +{ + runtestcase(); + return 0; +}