/* Copyright (C) 1999 Beau Kuiper this is a quick hack for ratiotool, works ok but that is all warning, this code is not buffer overflow safe. DONT RUN AS SETUID ROOT This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "ftpd.h" #define BYTERATIOMSK 1 #define FILERATIOMSK 2 #define BYTECREDITMSK 4 #define FILECREDITMSK 8 #define RELATIVE TRUE #define ABSOLUTE FALSE int lockarea(int fd, int pos, int len, int locktype, int do_wait) { struct flock lock; register int err; int lockt; lockt = (do_wait == TRUE ? F_SETLKW : F_SETLK); lock.l_type = locktype; lock.l_whence = SEEK_SET; lock.l_start = pos; lock.l_len = len; NOSIGNALINTR(err = fcntl(fd, lockt, &lock)); if (err == -1) { if (errno != EAGAIN) exit(0); else return(FALSE); } return(TRUE); } void lock_n_read(RATIOHANDLE *rh) { if (rh->ratiofilefd != 0) { lockarea(rh->ratiofilefd, rh->filepos, sizeof(RATIOFILEDATA), F_WRLCK, TRUE); lseek(rh->ratiofilefd, rh->filepos, SEEK_SET); read(rh->ratiofilefd, rh->rdat, sizeof(RATIOFILEDATA)); } } void write_n_unlock(RATIOHANDLE *rh) { if (rh->ratiofilefd != 0) { lseek(rh->ratiofilefd, rh->filepos, SEEK_SET); write(rh->ratiofilefd, rh->rdat, sizeof(RATIOFILEDATA)); lockarea(rh->ratiofilefd, rh->filepos, sizeof(RATIOFILEDATA), F_UNLCK, TRUE); } } int decoderatiostr(char *ratstr, int *pa, int *pb) { return(sscanf(ratstr, "%d:%d", pa, pb) == 2); } RATIOHANDLE *getuserrh(char *filename, char *username) { static RATIOHANDLE rh; static RATIOFILEDATA filedata; int pos = 0; rh.rdat = &filedata; rh.ratiofilefd = open(filename, O_RDWR); if (rh.ratiofilefd == -1) ERRORMSGFATAL("couldn't open ratio file!"); lockarea(rh.ratiofilefd, pos, sizeof(RATIOFILEDATA), F_WRLCK, TRUE); while(read(rh.ratiofilefd, &filedata, sizeof(RATIOFILEDATA)) == sizeof(RATIOFILEDATA)) { lockarea(rh.ratiofilefd, pos, sizeof(RATIOFILEDATA), F_UNLCK, TRUE); if (strcmp(filedata.username, username) == 0) { rh.filepos = pos; return(&rh); } pos += sizeof(RATIOFILEDATA); lockarea(rh.ratiofilefd, pos, sizeof(RATIOFILEDATA), F_WRLCK, TRUE); } close(rh.ratiofilefd); return(NULL); } /* this just returns a pointer to the end of the file, and checks to make user the user doesn't exist yet */ RATIOHANDLE *adduserrh(char *filename, char *username) { static RATIOHANDLE rh; static RATIOFILEDATA filedata; int pos = 0; rh.rdat = &filedata; rh.ratiofilefd = open(filename, O_RDWR | O_CREAT, 0600); if (rh.ratiofilefd == -1) ERRORMSGFATAL("couldn't open ratio file!"); lockarea(rh.ratiofilefd, pos, sizeof(RATIOFILEDATA), F_WRLCK, TRUE); while(read(rh.ratiofilefd, &filedata, sizeof(RATIOFILEDATA)) == sizeof(RATIOFILEDATA)) { lockarea(rh.ratiofilefd, pos, sizeof(RATIOFILEDATA), F_UNLCK, TRUE); if (strcmp(filedata.username, username) == 0) { close(rh.ratiofilefd); return(NULL); } pos += sizeof(RATIOFILEDATA); lockarea(rh.ratiofilefd, pos, sizeof(RATIOFILEDATA), F_WRLCK, TRUE); } rh.filepos = pos; return(&rh); } void usage(void) { printf("\nMuddleftpd Ratio File Editor.\n\n"); printf("Usage: ratiotool [options]\n\n"); printf(" -f ratiofile The filename of the ratio file to modify\n"); printf(" -a username Adds a new username\n"); printf(" -e username Edits the properties of a username\n"); printf(" -d username Scrubs the username from the ratio file\n"); printf(" -r fileratio Sets a new file ratio for the user.\n"); printf(" -R byteratio Sets a new byte ratio for the user.\n"); printf(" -c files Sets a new file credit for the user.\n"); printf(" -C bytes Sets a new byte credit for the user.\n"); printf(" -i username Gets information about user.\n"); printf(" -h Displays this usage message.\n"); printf(" -V Displays version information.\n\n"); printf("You must specify -f and only one of -a, -e, -d or -i when running ratiotool\n\n"); exit(0); } void showinfo(RATIOHANDLE *rh) { printf("Showing ratio information for user '%s'\n", rh->rdat->username); printf("\nThis user has the following download credits:\n\n"); if (rh->rdat->flags & BYTECREDITS) printf("%15lld bytes\n", rh->rdat->downloadcredits / rh->rdat->byteoutmult); if (rh->rdat->flags & FILECREDITS) printf("%15d files\n", rh->rdat->filecredits / rh->rdat->fileoutmult); if (rh->rdat->flags == 0) printf("This person does not use ratios and has no credits\n"); printf("\nThis user is subject to the following ratios:\n\n"); if (rh->rdat->flags & BYTECREDITS) printf("Byte ratio: %d:%d (ie %d bytes uploaded = %d download bytes)\n", rh->rdat->byteinmult, rh->rdat->byteoutmult, rh->rdat->byteoutmult, rh->rdat->byteinmult); if (rh->rdat->flags & FILECREDITS) printf("File ratio: %d:%d (ie %d files uploaded = %d download files)\n", rh->rdat->fileinmult, rh->rdat->fileoutmult, rh->rdat->fileoutmult, rh->rdat->fileinmult); if (rh->rdat->flags == 0) printf("This person does not use ratios.\n"); printf("\n\n"); } void adduser(char *rationame, char *username, char *byteratio, char *fileratio, char *filecount, char *bytecount) { int flags = 0; int upb, downb, upf, downf; int initalf; long long int initalb; RATIOHANDLE *rh; if (byteratio == NULL) { byteratio = (char *)malloc(4096); printf("Enter a byte ratio for the user (down:up or 'none') : "); fgets(byteratio, 4096, stdin); byteratio[strlen(byteratio) - 1] = 0; } if (fileratio == NULL) { fileratio = (char *)malloc(4096); printf("Enter a file ratio for the user (down:up or 'none') : "); fgets(fileratio, 4096, stdin); fileratio[strlen(fileratio) - 1] = 0; } if (strcasecmp(byteratio, "none") != 0) flags |= BYTECREDITS; if (strcasecmp(fileratio, "none") != 0) flags |= FILECREDITS; if (flags & BYTECREDITS) { if (bytecount == NULL) { bytecount = (char *)malloc(4096); printf("Enter an inital byte credit for the user (bytes) : "); fgets(bytecount, 4096, stdin); bytecount[strlen(bytecount) - 1] = 0; } if (sscanf(byteratio, "%d:%d", &upb, &downb) != 2) ERRORMSGFATAL("could not decode byte ratio"); if (sscanf(bytecount, "%lld", &initalb) != 1) ERRORMSGFATAL("could not decode inital byte credits"); initalb *= downb; } if (flags & FILECREDITS) { if (filecount == NULL) { filecount = (char *)malloc(4096); printf("Enter a inital file credit for the user : "); fgets(filecount, 4096, stdin); filecount[strlen(filecount) - 1] = 0; } if (sscanf(fileratio, "%d:%d", &upf, &downf) != 2) ERRORMSGFATAL("could not decode file ratio"); printf("%d\n", sscanf(filecount, "%d", &initalf)); if (sscanf(filecount, "%d", &initalf) != 1) ERRORMSGFATAL("could not decode inital file credits"); initalf *= downf; } rh = adduserrh(rationame, username); if (rh == NULL) ERRORMSGFATAL("user by that name already exists in the ratio file"); lock_n_read(rh); memset(rh->rdat, 0, sizeof(RATIOFILEDATA)); strncpy(rh->rdat->username, username, MAXNAMELEN); rh->rdat->flags = flags; if (flags & FILECREDITS) { rh->rdat->filecredits = initalf; rh->rdat->fileinmult = (short int)upf; rh->rdat->fileoutmult = (short int)downf; } if (flags & BYTECREDITS) { rh->rdat->downloadcredits = initalb; rh->rdat->byteinmult = (short int)upb; rh->rdat->byteoutmult = (short int)downb; } write_n_unlock(rh); close(rh->ratiofilefd); printf("Adding user to ratio file successful!\n"); } void edituser(char *rationame, char *username, char *byteratio, char *fileratio, char *filecount, char *bytecount, int edit_mask) { int upb, downb, upf, downf; int initalf; int modf = 0; int modb = 0; long long int initalb; RATIOHANDLE *rh; #define BYTERATIOMSK 1 #define FILERATIOMSK 2 #define BYTECREDITMSK 4 #define FILECREDITMSK 8 /* decode arguments */ if (edit_mask & BYTERATIOMSK) if (strcasecmp(byteratio, "none") != 0) if (sscanf(byteratio, "%d:%d", &upb, &downb) != 2) ERRORMSGFATAL("could not decode byte ratio"); if (edit_mask & BYTECREDITMSK) { if (bytecount[0] == 'r') { bytecount++; modb = RELATIVE; } else modb = ABSOLUTE; if (sscanf(bytecount, "%lld", &initalb) != 1) ERRORMSGFATAL("could not decode byte credits"); } if (edit_mask & FILERATIOMSK) if (strcasecmp(fileratio, "none") != 0) if (sscanf(fileratio, "%d:%d", &upf, &downf) != 2) ERRORMSGFATAL("could not decode file ratio"); if (edit_mask & FILECREDITMSK) { if (filecount[0] == 'r') { modf = RELATIVE; filecount++; } else modf = ABSOLUTE; if (sscanf(filecount, "%d", &initalf) != 1) ERRORMSGFATAL("could not decode file credits"); } rh = getuserrh(rationame, username); if (rh == NULL) ERRORMSGFATAL("Username is not in ratio file."); lock_n_read(rh); if (edit_mask & BYTERATIOMSK) { if (strcasecmp(byteratio, "none") == 0) rh->rdat->flags &= FILECREDITS; else { /* this updates the ratio and the credits */ rh->rdat->flags |= BYTECREDITS; rh->rdat->downloadcredits /= rh->rdat->byteoutmult; rh->rdat->byteinmult = (short int)upb; rh->rdat->byteoutmult = (short int)downb; rh->rdat->downloadcredits *= rh->rdat->byteoutmult; } } if (edit_mask & FILERATIOMSK) { if (strcasecmp(fileratio, "none") == 0) rh->rdat->flags &= BYTECREDITS; else { /* this updates the ratio and the credits */ rh->rdat->flags |= FILECREDITS; rh->rdat->filecredits /= rh->rdat->fileoutmult; rh->rdat->fileinmult = (short int)upf; rh->rdat->fileoutmult = (short int)downf; rh->rdat->filecredits *= rh->rdat->fileoutmult; } } if (edit_mask & BYTECREDITMSK) { if (modb == ABSOLUTE) rh->rdat->downloadcredits = initalb * rh->rdat->byteoutmult; else rh->rdat->downloadcredits += initalb * rh->rdat->byteoutmult; } if (edit_mask & FILECREDITMSK) { if (modf == ABSOLUTE) rh->rdat->filecredits = initalf * rh->rdat->fileoutmult; else rh->rdat->filecredits += initalf * rh->rdat->fileoutmult; } write_n_unlock(rh); close(rh->ratiofilefd); printf("Editing user in ratio file successful!\n"); } void deluser(char *ratiofile, char *username) { RATIOHANDLE *rh; rh = getuserrh(ratiofile, username); if (rh == NULL) ERRORMSGFATAL("Username is not in ratio file!"); lock_n_read(rh); rh->rdat->username[0] = 0; write_n_unlock(rh); close(rh->ratiofilefd); printf("Deleting user from ratio file successful!\n"); } int main(int argc, char **argv) { int ch; char *ratiofile = NULL; char *username = NULL; char *byteratio = NULL; char *fileratio = NULL; char *bytecount = NULL; char *filecount = NULL; int add_user = FALSE; int delete_user = FALSE; int edit_user = FALSE; int get_info = FALSE; int edit_mask = FALSE; extern char *optarg; /* stolen from smbpasswd - checking for setuid root! */ /* Check the effective uid - make sure we are not setuid */ if ((geteuid() == (uid_t)0) && (getuid() != (uid_t)0)) ERRORMSGFATAL("ratiotool must *NOT* be setuid root.\n"); while ((ch = getopt(argc, argv, "f:a:d:e:r:R:c:C:i:hV")) != EOF) { switch(ch) { case 'V': showversion("ratiotool"); case 'f': ratiofile = optarg; break; case 'a': add_user = TRUE; username = optarg; break; case 'd': delete_user = TRUE; username = optarg; break; case 'e': edit_user = TRUE; username = optarg; break; case 'r': fileratio = optarg; edit_mask |= FILERATIOMSK; break; case 'R': byteratio = optarg; edit_mask |= BYTERATIOMSK; break; case 'c': filecount = optarg; edit_mask |= FILECREDITMSK; break; case 'C': bytecount = optarg; edit_mask |= BYTECREDITMSK; break; case 'i': username = optarg; get_info = TRUE; break; case 'h': usage(); break; default: usage(); } } if (!ratiofile) usage(); if (add_user) { if (edit_user || delete_user || get_info) usage(); adduser(ratiofile, username, byteratio, fileratio, filecount, bytecount); exit(0); } if (delete_user) { if (edit_user || get_info) usage(); deluser(ratiofile, username); exit(0); } if (edit_user) { if (!edit_mask) usage(); edituser(ratiofile, username, byteratio, fileratio, filecount, bytecount, edit_mask); exit(0); } if (get_info) { RATIOHANDLE *rh; rh = getuserrh(ratiofile, username); if (rh == NULL) ERRORMSGFATAL("Username is not in ratio file!"); showinfo(rh); close(rh->ratiofilefd); exit(0); } usage(); return(0); }