Titel: New SFV Checker! Beitrag von: Kraklok am 20. Oktober 2003, 20:10:51 Here is a small, quick&dirty SFV-Checker I have built. It is a module for ProFTPd (so you need to recompile it). I know it is really a shitty piece of code :oops: Just tell me what you think of it, thanks ! :wink:
mod_sfv.c Code: /* * MOD_SFV.C v0.1 (oct.19 2003) * Author : Kraklok * * DESCRIPTION: * Module to enable SFV management with ProFTPd * * A really Quick&Dirty SFV-Checker, this module lacks much security * (checking for length and unallowed characters for example) and is * a great CPU-time/bandwidth/memory consummer. Also, this module is * not designed for multiple usage at the same time. (Now, you know * I'm not a good programmer and what Q&D means :-/ ) My only goal * was to write a ""challenger"" to FTPSFV ;-). * * There is no switch to enable or disable the module, you need to * recompile ProFTPd to enable/disable it. If enabled, MOD_SFV will * automatically check .SFV on LIST & DELE & STOR commands. All * filename checks are done without was sensitivity. * * Do not hesitate to modify it and redistribute it. It would be nice * to see a real good SFV-Checker. * The licence is: "do whatever you want with this code". But * remember: "I am not responsible for anything that can happen with * this module (corrupted uploads, file deletion, etc...)". * * COMPILING: * Put the file in ./contrib directory and launch: * ./configure --with-modules=mod_sfv[:more_modules] [more_options] && * make && make install * * It should compile without errors (with some warnings though...) */ /* * INCLUDES */ #include "conf.h" #include "mod_sfv.h" /* * DEFINITIONS */ static unsigned int SFVNbFiles=0; // Number of files found in .SFV static unsigned int SFVNbFileChecked=0; // Number of files already checked static struct SFVEntry *sfventry=NULL; // SFV definition variable /* * ResetSFVParam() * --------------- * DESCRIPTION: * Reset all the variables associated with SFV management * IN: * / * OUT: * / */ void ResetSFVParam(void) { struct SFVEntry *tempnext=NULL; // Temporary entry in the sfv file #ifdef DEBUG_MOD_SFV int i=0; #endif while(sfventry!=NULL) // Free the memory allocated for each sfv-entry { tempnext=sfventry->next; free(sfventry); sfventry=tempnext; #ifdef DEBUG_MOD_SFV i++; #endif } SFVNbFiles=0; // Set the variable back to zero SFVNbFileChecked=0; // #ifdef DEBUG_MOD_SFV // Show how many entries were fried: pr_response_add(R_212, "[DEBUG_MOD_SFV]%d entries released",i); #endif return; } /* * SFVLineContainsForbiddenChars() * ------------------------------- * DESCRIPTION: * Check if the first character of the current line in .SFV file is a forbidden character * IN: * firstchar(char)=first character of the current line in SFV file * OUT: * status(int)=tells if the character is allowed or not (status=0 => allowed, status!=0 => forbidden) */ int SFVLineContainsForbiddenChars(char firstchar) { char allowedchars[]={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f','g','h','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z'}; int status; for(status=0;status<sizeof(allowedchars);status++) { if(firstchar==allowedchars[status]) { status=0; break; } } return status; } /* * CheckFileAlreadyOk() * -------------------- * DESCRIPTION: * Check if a filename exists. * IN: * fname(char*)=pointer to a filename * OUT: * ret(int)=tells if the filename exists (ret=1 => exists, ret=0 => cannot find the filename) */ int CheckFileAlreadyOk(char *fname) { DIR *dir; struct dirent *directory; int ret=0; // Open working directory if((dir=opendir(session.cwd))==NULL) return ret; // Read each entity within the working dir. while((directory=readdir(dir))!=NULL) { // Test if filename exists if(strncasecmp(directory->d_name,fname,strlen(directory->d_name))==0) { #ifdef DEBUG_MOD_SFV pr_response_add(R_212, "[DEBUG_MOD_SFV]%s exists",fname); #endif ret=1; break; } } closedir(dir); return ret; } /* * DeleteINFODIR() * --------------- * DESCRIPTION: * Deletes INFO-DIR ([MySERV]-blahblah-[MySERV] directory). * !! ASSUMES this call is followed by a LIST-command * IN: * cmd(cmd_rec*)=[look at ProFTPd Dev.Guide for more infos] * OUT: * MODRET=[look at ProFTPd Dev.Guide for more infos] */ MODRET DeleteINFODIR(cmd_rec *cmd) { DIR *dir; struct dirent *directory; // Open working directory if((dir=opendir(session.cwd))==NULL) return DECLINED(cmd); // Read each entity within the working dir. while((directory=readdir(dir))!=NULL) { // Test if beginning & end tags are present in current entity // (!! this means a file named with INFODIRTAG_BEGIN & INFODIRTAG_END would make it fail !!) if(strncmp(directory->d_name,INFODIRTAG_BEGIN,strlen(INFODIRTAG_BEGIN))==0 && strncmp((directory->d_name)+strlen(directory->d_name)-strlen(INFODIRTAG_END),INFODIRTAG_END,strlen(INFODIRTAG_END))==0) { // If it matches, delete the entity... rmdir(directory->d_name); #ifdef DEBUG_MOD_SFV pr_response_add(R_212, "[DEBUG_MOD_SFV]%s deleted",directory->d_name); #endif } } closedir(dir); return DECLINED(cmd); } /* * CreateMissingFiles() * -------------------- * DESCRIPTION: * Create [original_filename].missing file when the file misses * IN: * / * OUT: * / */ void CreateMissingFiles(void) { static struct SFVEntry *tempsfv=NULL; char missing[MAX_FNAME]; tempsfv=sfventry; // for each entry in 'sfventry', create a .MISSING_STRING file while(tempsfv) { sprintf(missing,"%s.%s",tempsfv->fname,MISSING_STRING); creat(missing,S_IWUSR); tempsfv=tempsfv->next; } } /* * ParseSFV() * ---------- * DESCRIPTION: * Find the .SFV file and parse it to fill teh sfventry list. * IN: * sfvname(char*)=pointer to the sfvfile. If NULL, the function will search for it. * OUT: * / */ void ParseSFV(char *sfvname) { DIR *dir; struct dirent *directory; FILE *fp; char sfvline[MAX_FNAME]; static struct SFVEntry *tempsfv=NULL,*rootsfv=NULL; ResetSFVParam(); // Do we need to search for the .SFV file ? if(sfvname==NULL) { if((dir=opendir(session.cwd))==NULL) return; while((directory=readdir(dir))!=NULL) { if(strlen(directory->d_name) > 4) { if(strncasecmp((directory->d_name)+strlen(directory->d_name)-4,".SFV",4)==0) { sfvname=directory->d_name; break; } } } closedir(dir); } if((fp=fopen(sfvname,"rt"))==NULL) return; while(1) { if(feof(fp)) break; // while not EOF... if(fgets(sfvline,MAX_FNAME,fp)==NULL) break; // try to extract a line from .SFV file if(SFVLineContainsForbiddenChars(sfvline[0])==0) { // We found a valid SFV line : parse it... if(((struct SFVEntry*)tempsfv=malloc(sizeof(struct SFVEntry)))!=NULL) { tempsfv->next=NULL; if(sscanf(sfvline,"%s %8lX",tempsfv->fname,&(tempsfv->crc))>0) { #ifdef DEBUG_MOD_SFV pr_response_add(R_212, "[DEBUG_MOD_SFV] %s found with CRC=%8lX",tempsfv->fname,tempsfv->crc); #endif // Check if the file is already upped (we assume that if it was upped, it is good :/) if(CheckFileAlreadyOk(tempsfv->fname)) { SFVNbFileChecked++; } // If not, add it to the list of needed files else { if(sfventry==NULL) // If this is the first entry: { sfventry=tempsfv; rootsfv=tempsfv; } else // If not, add it as a (*next) { rootsfv->next=tempsfv; rootsfv=rootsfv->next; } } SFVNbFiles++; // File counter tempsfv=NULL; } else // If we couldn't read an entry, we can free the memory { free(tempsfv); tempsfv=NULL; } } } } fclose(fp); if(SFVNbFiles!=SFVNbFileChecked) { sprintf(sfvline,"%s%dF-%d%%%s",INFODIRTAG_BEGIN,SFVNbFileChecked,(int)(((double)SFVNbFileChecked/(double)SFVNbFiles)*100),INFODIRTAG_END); mkdir(sfvline,S_IWUSR); CreateMissingFiles(); } else{ sprintf(sfvline,"%s%dF-Done!%s",INFODIRTAG_BEGIN,SFVNbFileChecked,INFODIRTAG_END); mkdir(sfvline,S_IWUSR); } #ifdef DEBUG_MOD_SFV pr_response_add(R_212, "[DEBUG_MOD_SFV]%s created",sfvline); pr_response_add(R_212, "[DEBUG_MOD_SFV]%d files in SFV",SFVNbFiles); pr_response_add(R_212, "[DEBUG_MOD_SFV]%d files already upped",SFVNbFileChecked); #endif return; } /* * CheckSFV() * ---------- * DESCRIPTION: * Checks the first .SFV file found in CWD & check if an uploaded file is good * IN: * cmd(cmd_rec*)=[look at ProFTPd Dev.Guide for more infos] * OUT: * MODRET=[look at ProFTPd Dev.Guide for more infos] */ MODRET CheckSFV(cmd_rec *cmd) { struct SFVEntry *tempnext=NULL,*tempprevious=NULL; char missing[MAX_FNAME]; unsigned int fnlength=0; DeleteINFODIR(cmd); #ifdef DEBUG_MOD_SFV pr_response_add(R_212, "[DEBUG_MOD_SFV]Upped file is %s",session.xfer.filename); #endif // Check if we are uploading if(session.xfer.filename!=NULL) { // We are uploading a whole directory (no LIST before STOR) if(sfventry==NULL) { // Could this be a .SFV we are uploading ? if((fnlength=strlen(session.xfer.filename))>4) if(strncasecmp((session.xfer.filename)+strlen(session.xfer.filename)-4,".SFV",4)==0) // Yes! ParseSFV(session.xfer.filename); } // Delete INFO-DIR because we need to recreate it if anything has changed tempnext=sfventry; tempprevious=tempnext; // For each entry in the sfv-entry list while(tempnext) { // if the file we uploaded match an entry of the list if(strncasecmp(tempnext->fname,session.xfer.filename,strlen(tempnext->fname))==0) { // Compute the CRC32 & compare it to the value in the .SFV if(tempnext->crc==ComputeCRC(session.xfer.filename)) { #ifdef DEBUG_MOD_SFV pr_response_add(R_212, "[DEBUG_MOD_SFV]CRC matches for %s",session.xfer.filename); #endif // Delete [original_filename].missing file when the original is up sprintf(missing,"%s.%s",tempnext->fname,MISSING_STRING); unlink(missing); // Delete from the sfv-entry list. if(tempnext==sfventry) sfventry=tempnext->next; else tempprevious->next=tempnext->next; free(tempnext); } // If there was a problem during teh upload, delete the file. else unlink(session.xfer.filename); break; } else{ tempprevious=tempnext; tempnext=tempnext->next; } } } // Else this is just a LIST ParseSFV(NULL); return DECLINED(cmd); } /* * Commands handling table */ static cmdtable sfv_cmdtab[] = { { PRE_CMD, C_LIST, G_NONE, CheckSFV, FALSE, FALSE }, { POST_CMD, C_STOR, G_NONE, CheckSFV, FALSE, FALSE }, { POST_CMD, C_DELE, G_NONE, DeleteINFODIR, FALSE, FALSE }, { 0, NULL } }; /* * Module definition */ module sfv_module = { NULL, NULL, 0x20, "sfv", NULL, sfv_cmdtab, NULL, NULL, NULL }; mod_sfv.h Code: /* * MOD_SFV.H v0.1 (oct.19 2003) * Author : Kraklok (+TL) * * DESCRIPTION: * Contains all the defines and other functions (ComputeCRC()) * */ /* * INCLUDES */ #include <sys/vfs.h> #include <sys/types.h> #include <dirent.h> /* * DEFINES */ #define DEBUG_MOD_SFV 1 // Sets debugging on #define MAX_FNAME 256 // Maximum filename length #define MISSING_STRING "missing" // String to add at the end of missing files #define INFODIRTAG_BEGIN "[MyServ]-" // Beginning tag for INFO-DIR #define INFODIRTAG_END "-[MyServ]" // Ending tag for INFO-DIR /* * STRUCTURES */ /* * struct SFVEntry * --------------- * DESCRIPTION: * Describes an entry of the SFV file. All entries are linked. */ struct SFVEntry{ char fname[MAX_FNAME]; // Filename to check unsigned long crc; // CRC32 associated with this file struct SFVEntry *next; // pointer to next SFV entry }; /* * FUNCTIONS */ /* * CRC() * ----- * DESCRIPTION: * Function used to compute CRC32 value. * IN: * crc(unsigned long)=CRC32 computed value * localread(long)=numbre of bytes in *buffer * buffer(char*)=pointer to content of file * OUT: * crc(long unsigned)=CRC32 computed value * * * Ripped from TL's FTPSFV * Thanks to TL (tl@bunker-werk.net) * http://www.bunker-werk.net/proftpd/ */ unsigned long CRC(unsigned long crc,long localread, char *buffer) { unsigned long CRCTABLE[]={0x00000000,0x77073096,0xee0e612c,0x990951ba,0x076dc419,0x706af48f, 0xe963a535,0x9e6495a3,0x0edb8832,0x79dcb8a4,0xe0d5e91e,0x97d2d988, 0x09b64c2b,0x7eb17cbd,0xe7b82d07,0x90bf1d91,0x1db71064,0x6ab020f2, 0xf3b97148,0x84be41de,0x1adad47d,0x6ddde4eb,0xf4d4b551,0x83d385c7, 0x136c9856,0x646ba8c0,0xfd62f97a,0x8a65c9ec,0x14015c4f,0x63066cd9, 0xfa0f3d63,0x8d080df5,0x3b6e20c8,0x4c69105e,0xd56041e4,0xa2677172, 0x3c03e4d1,0x4b04d447,0xd20d85fd,0xa50ab56b,0x35b5a8fa,0x42b2986c, 0xdbbbc9d6,0xacbcf940,0x32d86ce3,0x45df5c75,0xdcd60dcf,0xabd13d59, 0x26d930ac,0x51de003a,0xc8d75180,0xbfd06116,0x21b4f4b5,0x56b3c423, 0xcfba9599,0xb8bda50f,0x2802b89e,0x5f058808,0xc60cd9b2,0xb10be924, 0x2f6f7c87,0x58684c11,0xc1611dab,0xb6662d3d,0x76dc4190,0x01db7106, 0x98d220bc,0xefd5102a,0x71b18589,0x06b6b51f,0x9fbfe4a5,0xe8b8d433, 0x7807c9a2,0x0f00f934,0x9609a88e,0xe10e9818,0x7f6a0dbb,0x086d3d2d, 0x91646c97,0xe6635c01,0x6b6b51f4,0x1c6c6162,0x856530d8,0xf262004e, 0x6c0695ed,0x1b01a57b,0x8208f4c1,0xf50fc457,0x65b0d9c6,0x12b7e950, 0x8bbeb8ea,0xfcb9887c,0x62dd1ddf,0x15da2d49,0x8cd37cf3,0xfbd44c65, 0x4db26158,0x3ab551ce,0xa3bc0074,0xd4bb30e2,0x4adfa541,0x3dd895d7, 0xa4d1c46d,0xd3d6f4fb,0x4369e96a,0x346ed9fc,0xad678846,0xda60b8d0, 0x44042d73,0x33031de5,0xaa0a4c5f,0xdd0d7cc9,0x5005713c,0x270241aa, 0xbe0b1010,0xc90c2086,0x5768b525,0x206f85b3,0xb966d409,0xce61e49f, 0x5edef90e,0x29d9c998,0xb0d09822,0xc7d7a8b4,0x59b33d17,0x2eb40d81, 0xb7bd5c3b,0xc0ba6cad,0xedb88320,0x9abfb3b6,0x03b6e20c,0x74b1d29a, 0xead54739,0x9dd277af,0x04db2615,0x73dc1683,0xe3630b12,0x94643b84, 0x0d6d6a3e,0x7a6a5aa8,0xe40ecf0b,0x9309ff9d,0x0a00ae27,0x7d079eb1, 0xf00f9344,0x8708a3d2,0x1e01f268,0x6906c2fe,0xf762575d,0x806567cb, 0x196c3671,0x6e6b06e7,0xfed41b76,0x89d32be0,0x10da7a5a,0x67dd4acc, 0xf9b9df6f,0x8ebeeff9,0x17b7be43,0x60b08ed5,0xd6d6a3e8,0xa1d1937e, 0x38d8c2c4,0x4fdff252,0xd1bb67f1,0xa6bc5767,0x3fb506dd,0x48b2364b, 0xd80d2bda,0xaf0a1b4c,0x36034af6,0x41047a60,0xdf60efc3,0xa867df55, 0x316e8eef,0x4669be79,0xcb61b38c,0xbc66831a,0x256fd2a0,0x5268e236, 0xcc0c7795,0xbb0b4703,0x220216b9,0x5505262f,0xc5ba3bbe,0xb2bd0b28, 0x2bb45a92,0x5cb36a04,0xc2d7ffa7,0xb5d0cf31,0x2cd99e8b,0x5bdeae1d, 0x9b64c2b0,0xec63f226,0x756aa39c,0x026d930a,0x9c0906a9,0xeb0e363f, 0x72076785,0x05005713,0x95bf4a82,0xe2b87a14,0x7bb12bae,0x0cb61b38, 0x92d28e9b,0xe5d5be0d,0x7cdcefb7,0x0bdbdf21,0x86d3d2d4,0xf1d4e242, 0x68ddb3f8,0x1fda836e,0x81be16cd,0xf6b9265b,0x6fb077e1,0x18b74777, 0x88085ae6,0xff0f6a70,0x66063bca,0x11010b5c,0x8f659eff,0xf862ae69, 0x616bffd3,0x166ccf45,0xa00ae278,0xd70dd2ee,0x4e048354,0x3903b3c2, 0xa7672661,0xd06016f7,0x4969474d,0x3e6e77db,0xaed16a4a,0xd9d65adc, 0x40df0b66,0x37d83bf0,0xa9bcae53,0xdebb9ec5,0x47b2cf7f,0x30b5ffe9, 0xbdbdf21c,0xcabac28a,0x53b39330,0x24b4a3a6,0xbad03605,0xcdd70693, 0x54de5729,0x23d967bf,0xb3667a2e,0xc4614ab8,0x5d681b02,0x2a6f2b94, 0xb40bbe37,0xc30c8ea1,0x5a05df1b,0x2d02ef8d }; do{ crc=((crc>>8)&0xFFFFFF)^CRCTABLE[(unsigned char)((crc & 0xff)^*buffer++)]; }while(--localread); return crc; } /* * ComputeCRC() * ------------ * DESCRIPTION: * Function used to compute CRC32 value, given a filename. * IN: * fname(char*)=pointer to the filename * OUT: * crc(long unsigned)=CRC32 computed value * * * Ripped from TL's FTPSFV * Thanks to TL (tl@bunker-werk.net) * http://www.bunker-werk.net/proftpd/ */ unsigned long ComputeCRC(char *fname) { /* * Note: different buffer sizes may result in noticable * different performance depending on system, so feel * free to modify. */ #define BUFFERSIZE 65536*16 unsigned long crc=0xffffffff; FILE *f=NULL; long localread=0; char buffer[BUFFERSIZE]; if((f=fopen(fname,"rb"))!= NULL) { while((localread=fread(buffer,1,BUFFERSIZE,f))>0) { crc=CRC(crc,localread,buffer); } fclose(f); crc=crc^0xffffffff; } return crc; } (Thanks to TL for the SFV-Check code :wink:) Titel: New SFV Checker! Beitrag von: Wörsty am 21. Oktober 2003, 10:55:23 Zitat Just tell me what you think of it, thanks Sorry. I can't read c-Code :oops: But it looks good :roll: :wink: Titel: New SFV Checker! Beitrag von: Kraklok am 21. Oktober 2003, 13:24:21 :lol:
Thank you anyway :D Titel: New SFV Checker! Beitrag von: TL am 21. Oktober 2003, 21:49:12 hmm, I haven't yet looked closely to the code, but it REALLY consumes VERY MUCH cpu-time...
Also a few logical bugs seem to be in the code. What do you mean by " It would be nice to see a real good SFV-Checker." ? :?: Do you think ftpsfv is bad or need some enhancements? If you think so, just drop a mail or write your opinion here in the board, I will try to it better... :wink: Titel: New SFV Checker! Beitrag von: Kraklok am 22. Oktober 2003, 09:51:34 Hello TL!
I don't think FTPSFV is bad, it is a good piece of software but I don't like it :D 1- I don't like the ftpexecd daemon. I don't like having many processes running. 2- I don't like ftpsfvcheck.pl because I needed to install PERL (you will say I could have rewritten it :wink:) 3- I had to create a fifo-file (I don't like to mess my system with new files) 4- When I tried it, it checked the file with case-sensitivity and always failed (filename in lower case in .SFV et filename upped in uppercase). Maybe it is just a parameter to set but I didn't check... Anyway, this an open-design and so it is good and I'm sure many people are happy with it. :wink: I just would like to see FTPSFV to be more integrated to ProFPTd. :idea: Titel: New SFV Checker! Beitrag von: TL am 22. Oktober 2003, 18:21:00 Ok, I see...
The problem with the case-sensitive filenames ist fixed in the current version... ;) In my opinion it makes more sense to handle the .sfv checking asynchronly to the ftp-process, so the user/client can directly continue to work and has not to wait until the processing of the uploaded file has finished. That was my main reason for doing the .sfv checking via an external daemon and not via a module... If enough users are wanting ftpSFV integrated into proftpd, there will be a mod for proftpd... :!: |