Make your own free website on Tripod.com

nb.c


FTP-uploader



Home

Samples

DownloadHTTPServer

FTP-uploader

HighPerformanceAnonymousFTPServer

/*
        ftpup.c

        High performance FTP-uploader.

        Unix version(FreeBSD, Linux):
        
        cc ftpup.c nb.c mlib.c -o ftpup
        ./ftpup ftpuser:password@host[:port] [-ddirectory] [-h] file1 [... fileN]

        Windows version (MS Visual C++):

        cl /D__WIN_VERSION__ ftpup.c nb.c mlib.c ws2_32.lib
        ftpup.exe ftpuser:password@host[:port] [-ddirectory] [-h] file1 [... fileN]
    
        (c) /dev/www (wwwdev@mail.ru), 2006
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

#include "nb.h"


#define NAME_LEN        256
#define BUF_LEN                4096

#define TIME_OUT        15        /* Inactivity timeout, sec */
#define POLL_TIME        1000        /* Polling timeout, msec */

#define MAX_CLIENTS        30

#define WAIT_CONN        1
#define WAIT_PASS        2
#define WAIT_ENTER        3
#define WAIT_CD                4
#define WAIT_PASV        5
#define WAIT_STOR        6
#define WAIT_COMPLETE        7
#define WAIT_BINARY        8

#define CONN_CC                1        /* Control connection */
#define CONN_DC                2        /* Data connection */

char user[NAME_LEN];                /* FTP-user */
char password[NAME_LEN];        /* Password */
char host[NAME_LEN];                /* Destination host */
int port = 21;                        /* Port */
char directory[NAME_LEN];        /* Destination directory */
                                
typedef struct {                /* Data connection */
        int type;                /* CONN_DC */
        unsigned int activity;        /* time() of activity */
        NB_CONN *nbc;                /* Current connection */
        void *cc;                /* Pointer to control connection */
        FILE *fp;                /* File pointer */
} DATA_CONN;
                                
typedef struct {                /* Control connection */
        int type;                /* CONN_CC */
        unsigned int activity;        /* time() of activity */
        NB_CONN *nbc;                /* Current connection */
        DATA_CONN *dc;                /* Data connection */
        char *fname;                /* File to upload */
        int wait_for;                /* Current stage */
        char host[16];                /* Data connection ip (from PASV) */
        int port;                /* Data connection port */
} FTP_CONN;

char *ftp_answers[] = {
        "", "220", "331", "230", "250", "227", "150", "226", "\n226", "200", ""
};

enum FTP_ANSWERS {
        FTP_0, FTP_CONNECT, FTP_PASS_REQ, FTP_LOGGED_IN, FTP_CWD, FTP_PASV,
        FTP_STOR, FTP_COMPLETE,        FTP_COMPLETE1, FTP_BINARY, FTP_
};


char buf[BUF_LEN];

/*
        Delete right blanks
*/
char *rtrim(char *str, char *empty)
{
    char *p;
    int len;
    
    len = strlen(str);
    if(len){
        for(p = str + len - 1; strchr(empty, *p) && p >= str;)
            *p-- = '\0';
    }

    return(str);
}

/*
        Delete left blanks
*/
char *ltrim(char *str, char *empty)
{
    char *p, *s;
    
    if(strlen(str)){
        for(p = str; strchr(empty, *p) && *p; p++);
        for(s = str;; s++, p++){
            *s = *p;
            if(!*s)
                break;
        }
    }

    return(str);
}

/*
        Delete left and right blanks
*/
char *atrim(char *str, char *empty)
{
        return(ltrim(rtrim(str, empty), empty));
}

/*
        Extract token from buffer
*/
char *parse(char *buf, char *data, char *left_bound, char *right_bound)
{
        char *p, *dp;

        for(p = buf; *p && strchr(left_bound, *p); p++);
        for(dp = data; *p && !strchr(right_bound, *p);)
                *dp++ = *p++;
        *dp = '\0';
        
        return(data);
}

/*
        Split hostname[:port] to hostname and port
*/
void parse_host(char *str, char *ip, int *port)
{
    int i;

    ltrim(str, " \t");
    rtrim(str, " \t\r\n");
    for(i = 0; str[i] && str[i] != ':' && str[i] != ' ' && str[i] != '\t'; i++)
        ip[i] = str[i];
    ip[i] = '\0';
    ltrim(str + i, " :\t");
    i = atoi(str + i);
    if(i)
        *port = i;

}

/*
        Read data from file and put to outgoing buffer
*/
int read_file_block(DATA_CONN *dc)
{
        FTP_CONN *ftpc;
        int n;

        ftpc = (FTP_CONN *)dc->cc;
        dc->activity = ftpc->activity = time(NULL);
        
        n = fread(buf, 1, BUF_LEN, dc->fp);
        if(n < 0){
                fprintf(stderr, "File %s read error\n", ftpc->fname);
                ftpc->dc = NULL;
                nb_close(ftpc->nbc, 0);
                nb_disconnect(dc->nbc);
        }
        else {
                if(n < BUF_LEN){
                        ftpc->dc = NULL;
                        if(n)
                                nb_put_disconnect(dc->nbc, buf, n);
                        else
                                nb_disconnect(dc->nbc);
                        fclose(dc->fp);
                        dc->fp = NULL;
                }
                else
                        nb_put(dc->nbc, buf, n);
        }

        return 0;
}

/*
        Constructor/destructor for data connection
*/
int data_onconn(struct nb_conn *nbc, int status)
{
        DATA_CONN *dc;
        FTP_CONN *ftpc;

        dc = (DATA_CONN *)nbc->private_data;
        ftpc = dc ? (FTP_CONN *)dc->cc : NULL;
        if(!status){                                /* Constructor */
                dc->activity = ftpc->activity = time(NULL);
                if(NULL == (dc->fp = fopen(ftpc->fname, "rb"))){
                        fprintf(stderr, "File %s open error\n", ftpc->fname);
                        ftpc->dc = NULL;
                        nb_close(ftpc->nbc, 0);
                        return 1;
                }
                read_file_block(dc);
        }
        else {
                if(dc->fp)
                        fclose(dc->fp);
                if(ftpc)
                        ftpc->dc = NULL;
        }

        return 0;
}

/*
        on_empty callback for data connection
*/
int data_onempty(struct nb_conn *nbc)
{
        return(read_file_block((DATA_CONN *)nbc->private_data));
}

/*
        Constructor/destructor for control connection
*/
int ftp_onconn(struct nb_conn *nbc, int status)
{
        FTP_CONN *ftpc;

        ftpc = (FTP_CONN *)nbc->private_data;
        if(status){                        /* Destructor */
                if(ftpc->wait_for == WAIT_CONN)
                        fprintf(stderr, "Uploading %s: can't connect to %s:%d\n", 
                                        ftpc->fname, host, port);
                if(ftpc->dc)
                        nb_close(ftpc->dc->nbc, 0);
        }
        else
                ftpc->activity = time(NULL);

        return 0;
}
/*
        Create data connection
*/
void establish_dc(FTP_CONN *ftpc)
{

        if(NULL == (ftpc->dc = (DATA_CONN *)malloc(sizeof(DATA_CONN)))){
                fprintf(stderr, "Uploading %s: memory allocation error\n", ftpc->fname);
                nb_disconnect(ftpc->nbc);
                return;
        }
        memset(ftpc->dc, 0, sizeof(DATA_CONN));
        ftpc->dc->type = CONN_DC;
        ftpc->dc->cc = ftpc;
        ftpc->dc->activity = (unsigned int)time(NULL);
        if(nb_connect(&ftpc->dc->nbc, ftpc->host, ftpc->port, data_onconn, NULL, data_onempty, (void*)ftpc->dc)){
                free(ftpc->dc);
                ftpc->dc = NULL;
                fprintf(stderr, "Uploading %s: Internal error\n", ftpc->fname);
                nb_disconnect(ftpc->nbc);
        }

}

/*
        Incoming data handler for control connection
*/
int ftp_ondata(struct nb_conn *nbc)
{
        FTP_CONN *ftpc;
        char *p;
        int i1, i2, i3, i4, p1, p2;
        int n;

        ftpc = (FTP_CONN *)nbc->private_data;
        ftpc->activity = time(NULL);

        memset(buf, 0, BUF_LEN);
        n = nb_get(nbc, buf, BUF_LEN - 1);
        if(n <= 0){
                nb_disconnect(nbc);
                return 0;
        }

        while(1){
                if(ftpc->wait_for == WAIT_CONN){
                        if(buf != strstr(buf, ftp_answers[FTP_CONNECT])){
                                fprintf(stderr, "Uploading %s: bad server response(CONNECT) %s\n", 
                                                ftpc->fname, buf);
                                nb_disconnect(nbc);
                                break;
                        }
                        ftpc->wait_for = WAIT_PASS;
                        sprintf(buf, "USER %s\r\n", user);
                        break;
                }
                if(ftpc->wait_for == WAIT_PASS){
                        if(buf != strstr(buf, ftp_answers[FTP_PASS_REQ])){
                                fprintf(stderr, "Uploading %s: bad server response(USER) %s\n", 
                                                ftpc->fname, buf);
                                nb_disconnect(nbc);
                                break;
                        }
                        ftpc->wait_for = WAIT_ENTER;
                        sprintf(buf, "PASS %s\r\n", password);
                        break;
                }
                if(ftpc->wait_for == WAIT_ENTER){
                        if(buf != strstr(buf, ftp_answers[FTP_LOGGED_IN])){
                                fprintf(stderr, "Uploading %s: bad server response(PASS) %s\n", 
                                                ftpc->fname, buf);
                                nb_disconnect(nbc);
                                break;
                        }
                        ftpc->wait_for = WAIT_BINARY;
                        sprintf(buf, "TYPE I\r\n");
                        break;
                }
                if(ftpc->wait_for == WAIT_BINARY){
                        if(buf != strstr(buf, ftp_answers[FTP_BINARY])){
                                fprintf(stderr, "Uploading %s: bad server response(CWD) %s\n", 
                                                ftpc->fname, buf);
                                nb_disconnect(nbc);
                                break;
                        }
                        if(*directory){
                                ftpc->wait_for = WAIT_CD;
                                sprintf(buf, "CWD %s\r\n", directory);
                        }
                        else {
                                ftpc->wait_for = WAIT_PASV;
                                sprintf(buf, "PASV\r\n");
                        }
                        break;
                }
                if(ftpc->wait_for == WAIT_CD){
                        if(buf != strstr(buf, ftp_answers[FTP_CWD])){
                                fprintf(stderr, "Uploading %s: bad server response(CWD) %s\n", 
                                                ftpc->fname, buf);
                                nb_disconnect(nbc);
                                break;
                        }
                        ftpc->wait_for = WAIT_PASV;
                        sprintf(buf, "PASV\r\n");
                        break;
                }
                if(ftpc->wait_for == WAIT_PASV){
                        if(buf != strstr(buf, ftp_answers[FTP_PASV])){
                                fprintf(stderr, "Uploading %s: bad server response(PASV) %s\n", 
                                                ftpc->fname, buf);
                                nb_disconnect(nbc);
                                break;
                        }
                        if(NULL == (p = strchr(buf, '('))){
                                fprintf(stderr, "Uploading %s: bad sequence in server response(PASV) %s\n", 
                                                ftpc->fname, buf);
                                nb_disconnect(nbc);
                                break;
                        }
                        if(6 == sscanf(p, "(%u,%u,%u,%u,%u,%u)", &i1, &i2, &i3, &i4, &p1, &p2))
                                if(!(i1 > 255 || i2 > 255 || i3 > 255 || i4 > 255 || p1 > 255 || p2 > 255 ||
                                        (i1|i2|i3|i4) == 0 || (p1|p2) == 0)){
                                        sprintf(ftpc->host, "%d.%d.%d.%d", i1, i2, i3, i4);
                                        ftpc->port = p1*256 + p2;
                                        ftpc->wait_for = WAIT_STOR;
                                        sprintf(buf, "STOR %s\r\n", ftpc->fname);
                                        establish_dc(ftpc); /* Connect to data port */
                                        break;
                                }
                        fprintf(stderr, "Uploading %s: bad ip/port in server response(PASV) %s\n", 
                                        ftpc->fname, buf);
                        nb_disconnect(nbc);
                        break;
                }
                if(ftpc->wait_for == WAIT_STOR){
                        if(buf != strstr(buf, ftp_answers[FTP_STOR])){
                                fprintf(stderr, "Uploading %s: bad server response(STOR) %s\n", 
                                                ftpc->fname, buf);
                                nb_disconnect(nbc);
                                break;
                        }
                        if(strstr(buf, ftp_answers[FTP_COMPLETE1])){
                                printf("Uploading OK %s\n", ftpc->fname);
                                nb_disconnect(nbc);
                                break;
                        }
                        ftpc->wait_for = WAIT_COMPLETE;
                        return 0;
                }
                if(ftpc->wait_for == WAIT_COMPLETE){
                        if(buf != strstr(buf, ftp_answers[FTP_COMPLETE]))
                                fprintf(stderr, "Uploading %s: bad server response(COMPLETE) %s\n", 
                                        ftpc->fname, buf);
                        else 
                                printf("Uploading OK %s\n", ftpc->fname);
                        nb_disconnect(nbc);
                }
                break;
        }
        if(!nbc->disconnect)
                nb_put(nbc, buf, strlen(buf));

        return 0;
}


int usage(void)
{

        fprintf(stderr, 
                "Usage: ftpup user:password@host[:port] [-ddirectory] [-h] file1 [... fileN]\n"
                "-ddirectory - cd to directory\n"
                "-h          - this help screen\n"
                );

        return 1;                
}

                                /* nb.c internal variables */
extern NB_CONN *_nbc;
extern int nbc_max;

int main(int argc, char *argv[])
{
        char *p;
        int i;
        FTP_CONN *ftpc;
        DATA_CONN *dc;
        unsigned int ctime;

        if(argc < 3)
                return(usage());

        strcpy(buf, argv[1]);

        p = buf;
        parse(p, user, " ", ":");
        if(!*user)
                return(usage());
        p += strlen(user) + 1;
        parse(p, password, ":", "@");
        if(!*password)
                return(usage());
        p += strlen(password) + 1;
        parse_host(p, host, &port);

        *directory = '\0';        
                                        /* Init nb-library */
        nb_tcp(0, MAX_CLIENTS*2);
                                        /* Create uploading conenctions */
        for(i = 2; i < argc; i++){
                if(argv[i][0] == '-'){
                        if(argv[i][1] == 'd'){
                                strcpy(directory, argv[i] + 2);
                                continue;
                        }
                        if(argv[i][1] == 'h')
                                return(usage());
                        return(usage());
                }
                if(NULL == (ftpc = (FTP_CONN *)malloc(sizeof(FTP_CONN)))){
                        fprintf(stderr, "Memory allocation error\n");
                        return 2;
                }
                memset(ftpc, 0, sizeof(FTP_CONN));
                ftpc->type = CONN_CC;
                ftpc->fname = argv[i];
                ftpc->wait_for = WAIT_CONN;
                ftpc->activity = (unsigned int)time(NULL);
                if(nb_connect(&ftpc->nbc, host, port, ftp_onconn, ftp_ondata, NULL, (void*)ftpc)){
                        free(ftpc);
                        fprintf(stderr, "Internal error\n");
                        return 2;
                }
        }
                                                /*        Main loop */
        for(;;){
                nb_poll(POLL_TIME);
                if(!nb_cnt())
                        break;                        /* All connections done */
                ctime = time(NULL);
                for(i = 0; i < nbc_max; i++){        /* Check all connections */
                        if(_nbc[i].fd > 0){
                                if(*(int*)_nbc[i].private_data == CONN_CC){
                                        ftpc = (FTP_CONN *)_nbc[i].private_data;
                                        if(ctime - ftpc->activity > TIME_OUT){
                                                fprintf(stderr, "%s uploading timeout(Control connection)\n", 
                                                                ftpc->fname);
                                                nb_close(&_nbc[i], 1);
                                                continue;
                                        }
                                }
                                if(*(int*)_nbc[i].private_data == CONN_DC){
                                        dc = (DATA_CONN *)_nbc[i].private_data;
                                        ftpc = (FTP_CONN *)dc->cc;
                                        if(ctime - dc->activity > TIME_OUT){
                                                fprintf(stderr, "%s uploading timeout(Data connection)\n", 
                                                                ftpc->fname);
                                                nb_close(ftpc->nbc, 0);
                                                nb_close(&_nbc[i], 1);
                                        }
                                }
                        }
                }
        }
        nb_tcp_done();

        printf("Done\n");

        return 0;
}


(c) /dev/www, 2006