/*
    Copyright 2006 Luigi Auriemma    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 of the License, 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
    http://www.gnu.org/licenses/gpl.txt
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <ctype.h>
#include <sys/stat.h>
#ifdef WIN32
    #include <winsock.h>
    #include "winerr.h"
    #define close   closesocket
    #define sleep   Sleep
#else
    #include <unistd.h>
    #include <sys/socket.h>
    #include <sys/types.h>
    #include <arpa/inet.h>
    #include <netinet/in.h>
    #include <netdb.h>
#endif
#define VER     "0.1"
u_char *load_file(u_char *filename, int *size);
int create_socket(void);
u_char get_byte(u_char *data);
int get_num(u_char *data);
int create_rand_byte(u_char *data, int len, u_int *seed);
u_int resolv(char *host);
void std_err(void);
int main(int argc, char *argv[]) {
    struct  sockaddr_in peer,
                        peerl;
    u_int   seed;
    int     sd        = 0,
            i,
            size,
            randport  = 0,
            randbyte  = 0,
            loop      = 0,
            loopms    = 0,
            chr       = 0;
    u_short port,
            sport     = 0;
    u_char  *buff,
            *filename = NULL;
#ifdef WIN32
    WSADATA    wsadata;
    WSAStartup(MAKEWORD(1,0), &wsadata);
#endif
    setbuf(stdout, NULL);
    fputs("\n"
        "UDPSZ "VER"\n"
        "by Luigi Auriemma\n"
        "e-mail: aluigi@autistici.org\n"
        "web:    aluigi.org\n"
        "\n", stdout);
    if(argc < 4) {
        printf("\n"
            "Usage: %s [options] <host> <port> <pck_size>\n"
            "\n"
            "Options:\n"
            "-b BYTE    fill the packet with byte BYTE, which can be a char or a hex\n"
            "           example: -b a   or   -b 0x61   or   -b 61\n"
            "-r         random packet content\n"
            "-s         packet filled with incremental byte (use -b for the start byte)\n"
            "-p PORT    use source port PORT\n"
            "-R         random source port\n"
            "-S         sequential source port\n"
            "-f FILE    load the content of the packet from FILE, the size will be\n"
            "           truncated at pck_size, set it to -1 for using the file size\n"
            "-l MS      send infinite packets with a delay of MS milliseconds\n"
            "\n", argv[0]);
        exit(1);
    }
    argc -= 3;
    for(i = 1; i < argc; i++) {
        switch(argv[i][1]) {
            case 'b': chr      = get_byte(argv[++i]);   break;
            case 'r': randbyte = 1;                     break;
            case 's': randbyte = 2;                     break;
            case 'p': sport    = get_num(argv[++i]);    break;
            case 'R': randport = 1;                     break;
            case 'S': randport = 2;                     break;
            case 'l': {
                loop   = 1;
                loopms = get_num(argv[++i]);
                } break;
            case 'f': filename = argv[++i];             break;
            default: {
                printf("\nError: wrong command-line argument (%s)\n\n", argv[i]);
                exit(1);
                } break;
        }
    }
    port                 = get_num(argv[argc + 1]);
    size                 = get_num(argv[argc + 2]);
    peer.sin_addr.s_addr = resolv(argv[argc]);
    peer.sin_port        = htons(port);
    peer.sin_family      = AF_INET;
    printf("- target   %s : %hu\n",
        inet_ntoa(peer.sin_addr), port);
    seed = time(NULL);
    if(filename) {
        buff = load_file(filename, &size);
    } else {
        buff = malloc(size);
        if(!buff) std_err();
        if(!randbyte) {
            memset(buff, chr, size);
        }
    }
    printf("- packet size:  %d\n", size);
    if(randport) {
        peerl.sin_addr.s_addr = INADDR_ANY;
        peerl.sin_port        = htons(~seed);
        peerl.sin_family      = AF_INET;
    } else {
        sd = create_socket();
    }
    if(sport && !randport) {
        peerl.sin_addr.s_addr = INADDR_ANY;
        peerl.sin_port        = htons(sport);
        peerl.sin_family      = AF_INET;
        printf("- source port:  %hu\n", sport);
        if(bind(sd, (struct sockaddr *)&peerl, sizeof(peerl))
          < 0) std_err();
    }
#ifndef WIN32
    loopms *= 1000;
#endif
    printf("- send packets: ");
    for(;;) {
        if(randbyte == 2) {
            memset(buff, chr++, size);
        } else if(randbyte == 1) {
            create_rand_byte(buff, size, &seed);
        }
        if(randport) {
            sd = create_socket();
            do {
                if(randport == 2) {                 // sequential
                    peerl.sin_port = htons(sport++);
                } else {
                    peerl.sin_port += seed;         // not really random
                }
            } while(bind(sd, (struct sockaddr *)&peerl, sizeof(peerl)) < 0);
        }
        if(sendto(sd, buff, size, 0, (struct sockaddr *)&peer, sizeof(peer))
          < 0) std_err();
        fputc('.', stdout);
        if(randport) {
            close(sd);
        }
        if(!loop) break;
#ifdef WIN32
        sleep(loopms);
#else
        usleep(loopms);
#endif
    }
    if(!randport) {
        close(sd);
    }
    free(buff);
    printf("\n- finished\n");
    return(0);
}
u_char *load_file(u_char *filename, int *size) {
    struct  stat    xstat;
    FILE    *fd;
    u_char  *buff;
    printf("- load file:    %s\n", filename);
    fd = fopen(filename, "rb");
    if(!fd) std_err();
    fstat(fileno(fd), &xstat);
    if((*size < 0) || (*size > xstat.st_size)) {
        *size = xstat.st_size;
    }
    buff = malloc(*size);
    if(!buff) std_err();
    fread(buff, *size, 1, fd);
    fclose(fd);
    return(buff);
}
int create_socket(void) {
    int     sd,
            on = 1;
    sd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    if(sd < 0) std_err();
    setsockopt(sd, SOL_SOCKET, SO_BROADCAST, (char *)&on, sizeof(on));  // useful?
    return(sd);
}
u_char get_byte(u_char *data) {
    int     chr;
    if(strlen(data) == 1) {
        return(data[0]);
    }
    if(tolower(data[1]) == 'x') {
        data += 2;
    } else if(tolower(data[0]) == 'x') {
        data++;
    }
    sscanf(data, "%x", &chr);
    return(chr);
}
int get_num(u_char *data) {
    int     num;
    if(tolower(data[1]) == 'x') {
        sscanf(data + 2, "%x", &num);
    } else if(tolower(data[0]) == 'x') {
        sscanf(data + 1, "%x", &num);
    } else {
        sscanf(data, "%d", &num);
    }
    return(num);
}
int create_rand_byte(u_char *data, int len, u_int *seed) {
    u_int   rnd;
    u_char  *p;
    rnd = *seed;
    p   = data;
    while(len--) {
        rnd = (rnd * 0x343FD) + 0x269EC3;
        rnd >>= 3;      // useless???
        *p++ = rnd & 0xff;
    }
    *seed = rnd;
    return(p - data);
}
u_int resolv(char *host) {
    struct  hostent *hp;
    u_int   host_ip;
    host_ip = inet_addr(host);
    if(host_ip == INADDR_NONE) {
        hp = gethostbyname(host);
        if(!hp) {
            printf("\nError: Unable to resolv hostname (%s)\n", host);
            exit(1);
        } else host_ip = *(u_int *)hp->h_addr;
    }
    return(host_ip);
}
#ifndef WIN32
    void std_err(void) {
        perror("\nError");
        exit(1);
    }
#endif