LINUX.ORG.RU

работа с ftpd bsd sockets

 , ,


0

1

Пишу ftp клиент, пока на bsd sockets. Не понимаю как в ftp клиентах обходят такой вот момент: recv возвращает нуль когда либо соединение закрыли на стороне сервера либо данные закончились. Делаю что-то вроде

do
{
        size_read = recv(client_socket, buf, BUF_SIZE, 0);
        buf[size_read]='\0';
        printf("%s",buf);
}while(size_read > 0);

Ну и на очередном recv программа останавливается и ждет очередных данных от сервера. Как обойти такой момент, попытки читать исходники netkit пока не помогли

★★

man select, man epoll и потом используй libev

true_admin ★★★★★
()

Вы язык не указали, фрагмент похож на СИ.

«recv возвращает нуль когда либо соединение закрыли на стороне сервера
либо данные закончились.»

Нет, только если закрыли.
Если речь идет о сокете данных, то вроде должно работать.
Если речь идет о сокете команд, то этот сокет не закрывается и ноль Вы
не получите. Надо парсить принятые данные.
Обычно это одна строка, но есть исключение.
Формат обычного ответа:

"215 UNIX Type: L8\r\n"
Другой формат, если за кодом ответа тире:
"214-help
LIST  RETR  STOR  QUIT  USER  PASS  NOOP  TYPE
PORT  PASV  HELP  FEAT  SYST  PWD   SIZE  ABOR
DELE  RMD   MKD   RNFR  RNTO  CDUP  CWD
214 end\r\n"

oleg_2
()
Ответ на: комментарий от oleg_2

спасибо. Да, это С. Речь идет о сокете команд. Кажется забавным парсить строки в таких протоколах(А что если завтра формат строк изменят...), теперь работает. Правда следом проблема возникла. Делаю

printf("sending SYST command\n");
    char *syst_string = strdup("SYST\n");
    send(client_socket,syst_string,strlen(syst_string),0);
    size_read = recv(client_socket, buf, 250, 0);
    buf[size_read] = '\0';
    printf("buf: %s \n",buf);
Получаю
sending SYST command
buf: 500 '': command not understood.

В wireshark:

15198	123062.980228000	127.0.0.1	127.0.0.1	FTP	71	Request: SYST
15200	123062.980395000	127.0.0.1	127.0.0.1	FTP	99	Response: 500 '': command not understood.
если использовать утилиту ftp то там аналогичный пакет нормально воспринимается.

blokant ★★
() автор топика
Ответ на: комментарий от blokant

Я написал простой FTP-сервер. Команды серверу приходят только однострочные,
значит парсер попроще, типа «КОМАНДА\r\n» или «КОМАНДА аргумент\r\n», например

"CWD all_funk\r\n" -перейти в каталог all_funk
Клиент тоже должен уметь такие распарсивать. Пример ответа на команду PWD:
257 "/usr/home/test/all_funk" is the current directory
Тут надо код ответа выпарсить 257 и полное имя каталога.

Почему ошибка команды SYST я не знаю. Может быть нужно сначала авторизоваться.
Последовательность команд анонимного подключения я не знаю, не интересовался.
А авторизованное смотрю по тотал-командеру, в нем справа вверху окно есть.
Вот последовательность:

220 FTP server ready   -это сервер сразу при подключении клиента выдает.
USER test
331 Password required.
PASS ***********
230 Log on network.
Вот при подключении должен бы сразу притти положительный ответ.
Несоблюдение последовательности нарушает работу. Клиент и сервер
должны уметь при нарушении синхронизироваться, т. е. пропустить все ненужные
команды и ответы. В этом, помоему, больше роль клиента.
Все команды, не требующие создания канала данных, отвечаются одним ответом.
А команды, работающие с каналом данных, отвечаются одним или двумя ответами - перед
отправкой данных, и по завершении. Например:
RETR start_sokol.sh
150 Opening BINARY mode data connection
226 Transfer complete

RETR .rhosts
451 Local error encountered

LIST
150 Here comes the directory listing
226 Transfer complete

oleg_2
()
Ответ на: комментарий от oleg_2

Вот нашел:

(Цитата) http://ln.com.ua/~openxs/projects/man/uman002.html

-c Подавляет сообщение SYST. Эта опция используется для предотвращения аварийного отказа удаленного сервера, который не обрабатывает это сообщение и не может работать с неизвестными сообщениями. Если первая же команда после подключения к удаленному серверу приводит к сообщению о том, что сервер отключил соединение, добавьте опцию -c в командную строку ftp и повторите запрос. (конец цитаты)

Дайте команду HELP, выведет список команд, которые понимает сервер.

oleg_2
()
Ответ на: комментарий от oleg_2

-c не помогает (сервер не понимает команду и сбрасывает соединение), прчем если послать USER <username>\n то тоже не понимает. HELP не понимает, залогинивание, вроде, прокатывает:

230- The programs included with the Debian GNU/Linux system are free software;
230- the exact distribution terms for each program are described in the
230- individual files in /usr/share/doc/*/copyright.
230- 
230- Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
230- permitted by applicable law.
230 User nexus logged in.

ftp_c.c


#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#define BUF_SIZE 1024
#define PORT_NUMBER 21  // for ftp command socket
//char server_address_string[] = "192.168.0.21";
int main(int argc, char** argv)
{

    if(argv[1] == NULL)
    {
        printf("Enter valid IP address as argument for program i.e. ftp_client 192.168.0.1 \n");
        exit(1);
    }
    char *server_address_string = malloc(strlen(argv[1]));
    strcpy(server_address_string,argv[1]);
    printf("Started client with server address = %s\n", server_address_string);

    int client_socket; // socket's descriptor for client 
    struct sockaddr_in addr; // address of the server 
    char buf[1024]; // buffer for incomming message
    int bytes_read; // amount of bytes

    client_socket = socket(AF_INET, SOCK_STREAM, 0);
    if(client_socket < 0)
    {
        perror("can not create socket");
        exit(1);
    }
    
    addr.sin_family = AF_INET; // address family - internet socket
    addr.sin_port = htons(PORT_NUMBER);
    addr.sin_addr.s_addr = inet_addr(server_address_string);
    if(connect(client_socket,(struct sockaddr*)&addr,sizeof(addr)) < 0)
    {
        printf("Can not connect!\n");
        exit(2);
    }
    printf("connected.\n");
   // client_socket = accept(server_socket,0,0);
    int size_read = 0;
    size_read = recv(client_socket, buf, 100, 0);
    {
   //     printf("size_read = %d\n",size_read);
        //buf[size_read] = '\0'; // for not printing rubbish
        printf("%s",buf);
    }
    printf("sending username\n");
    char *username = strdup("USER nexus\n");
    send(client_socket,username,strlen(username),0);

    size_read = recv(client_socket, buf, 100, 0);
    {
        buf[size_read] = '\0';

        printf("%s",buf);
    }
    
    char *passwd = strdup("PASS nexus\n");
    send(client_socket,passwd,strlen(passwd)+1,0);
    char *logged_string = strdup("logged in.");
    size_read = 0;
    do
    {
        size_read = recv(client_socket, buf, BUF_SIZE, 0);
     // printf("size_read : %d\n" , size_read);
      buf[size_read]='\0';
      printf("%s",buf);
      
   //   printf("check_string: %s\n",&buf[strlen(buf) - 12]);
   //   printf("loggedstring: %s\n",logged_string);
      if((strstr(&buf[strlen(buf) - 12],logged_string)) != NULL) // if user logged in
      {
          printf("break works\n");

          break;
      }
    }while(size_read > 0);
    


    printf("sending HELP command\n");
    char *passvmode_string = strdup("HELP -c\n");
    send(client_socket,passvmode_string,strlen(passvmode_string),0);
    size_read = recv(client_socket, buf, 250, 0);
    buf[size_read] = '\0';
    printf("buf: %s \n",buf);
 
      
      return 0;
}
 
В итоге

Started client with server address = 127.0.0.1
connected.
220 asusp53s FTP server (Version 6.4/OpenBSD/Linux-ftpd-0.17) ready.
sending username
331 Password required for nexus.
230- 
230- The programs included with the Debian GNU/Linux system are free software;
230- the exact distribution terms for each program are described in the
230- individual files in /usr/share/doc/*/copyright.
230- 
230- Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
230- permitted by applicable law.
230 User nexus logged in.
break works
sending HELP command
buf: 500 '': command not understood.

В сниффере видно, что команда уходит нормальная а сервер отвечает, что прислали пустую команду

blokant ★★
() автор топика
Ответ на: комментарий от blokant

Про опцию -c я имел ввиду, что ее надо дать в другом, фирменном клиенте.
Я Вашу программу переделал на свой манер, и у меня худо-бедно она работает.
На двух FTP-серверах пробовал.
Помоему Вы без надобности злоупотребляете malloc и strdup.

Вот это совершенно неправильно.

    char *logged_string = strdup("logged in.");
      ....
    if((strstr(&buf[strlen(buf) - 12],logged_string)) != NULL) // if user logged in
Это комментарий, комментарии произвольные у разных FTP-серверов.
Надо парсить код ответа
"230 User test logged in\r\n"
Вот 230 надо выпарсить. Для этой команды надо точно 230 получить.
Но для многих других команд надо взять только первую цыфру кода (здесь 2).

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>


//   gcc my_ftp_c.c -o my_ftp_c.cgi
//   ./my_ftp_c.cgi


#define BUF_SIZE 1024
#define PORT_NUMBER 21

const char *username="xxxx";
const char *pass="xxxxxx";

//------------------- main -------------------

int main(int argc, char** argv)
{
	int k;
    int client_socket; // socket's descriptor for client
    struct sockaddr_in addr; // address of the server
    char buf[BUF_SIZE]; // buffer for incomming message
    int bytes_read; // amount of bytes

    if(argc < 2)
    {
        printf("Enter valid IP address as argument for program i.e. ftp_client 192.168.0.1 \n");
        exit(1);
    }
    printf("Started client with server address = %s\n", argv[1]);

    client_socket = socket(AF_INET, SOCK_STREAM, 0);
    if(client_socket < 0)
    {
        perror("can not create socket");
        exit(1);
    }

    addr.sin_family = AF_INET; // address family - internet socket
    addr.sin_port = htons(PORT_NUMBER);
    addr.sin_addr.s_addr = inet_addr(argv[1]);
    if(connect(client_socket,(struct sockaddr*)&addr,sizeof(addr)) < 0)
    {
        printf("Can not connect!\n");
        exit(2);
    }
    printf("connected.\n");
    int size_read = 0;
    size_read = recv(client_socket, buf, 100, 0);
    if(size_read < 0){ printf("recv()<0"); exit(0);}
    buf[size_read] = '\0'; // for not printing rubbish
    printf("%s",buf);

    printf("sending username\n");
    snprintf(buf, sizeof(buf), "USER %s\n", username);
    k=send(client_socket, buf, strlen(buf), 0);
    if(k<0){ printf("send()<0"); exit(0);}

    size_read = recv(client_socket, buf, sizeof(buf) - 1, 0);
    if(size_read < 0){ printf("recv()<0"); exit(0);}
    buf[size_read] = '\0';
    printf("%s",buf);

    printf("sending pass\n");
    snprintf(buf, sizeof(buf), "PASS %s\n", pass);
    k=send(client_socket, buf, strlen(buf), 0);
    if(k<0){ printf("send()<0"); exit(0);}
    size_read = recv(client_socket, buf, sizeof(buf) - 1, 0);
    if(size_read < 0){ printf("recv()<0"); exit(0);}
    buf[size_read] = '\0';
    printf("%s",buf);

    printf("sending HELP command\n");
    snprintf(buf, sizeof(buf), "HELP\n", pass);
    k=send(client_socket, buf, strlen(buf), 0);
    if(k<0){ printf("send()<0"); exit(0);}
    size_read = recv(client_socket, buf, sizeof(buf) - 1, 0);
    if(size_read < 0){ printf("recv()<0"); exit(0);}
    buf[size_read] = '\0';
    printf("%s",buf);

    return 0;
}


Парсер надо, и он посложнее будет, чем серверный. Я попробую написать завтра.
Попробуйте эту слегка измененную программу, может заработает.

oleg_2
()
Ответ на: комментарий от oleg_2

спасибо, и за 230 и за прочие советы, я в си начинающий, хотя казалось бы с первого курса изучаю, и все же интересно почему в моей версии не катит

blokant ★★
() автор топика
Ответ на: комментарий от blokant

Это я запутал. Я имелл ввиду получить HELP другим встроенным UNIX-клиентом с
опцией -c, который ftp->.
Неработало из-за этой опции:

char *passvmode_string = strdup("HELP -c\n");
а надо
char *passvmode_string = strdup("HELP\n");

и строка 
        //buf[size_read] = '\0'; // for not printing rubbish
закомментирована, а не надо.
И назначение цикла do{}while; в конце программы я не понял.
И Вы не написали, работает ли?

recv не обязательно возвращает всё сообщение целиком, оно может притти кусками.
Парсер должен выбрать всё сообщение, пусть за несколько раз, но всё до последнего
байта. Он должен определить конец сообщения и более не вызывать recv, а то
подвиснет в ожидании данных, которых никогда не будет.
Распарсить должен такие форматы (примеры):

1.
"230 User test logged in\r\n"

2.
"257 "/usr/home/test/all_funk" is the current directory\r\n"

3. 
"214-help\r\n
LIST  RETR  STOR  QUIT  USER  PASS  NOOP  TYPE\r\n
PORT  PASV  HELP  FEAT  SYST  PWD   SIZE  ABOR\r\n
DELE  RMD   MKD   RNFR  RNTO  CDUP  CWD\r\n
214 end\r\n"
В первом примере нужен только код.
Во втором примере должен выпарсить код ответа и аргумент.
В третьем - код и , наверно, сам список.

oleg_2
()
Ответ на: комментарий от oleg_2

я понимаю, что сообщение может приходить кусками, поэтому надо вызывать recv несколько раз, пока не получим все данные, вот для этого я и хотел do\while сделать, ведь как иначе можно получить большое(фрагментированное) сообщение

blokant ★★
() автор топика
Ответ на: комментарий от blokant

ваша программа, кстати, пока не могу понять почему, не печатает результат последнего запроса, хотя в сниффере видно что все проходит нормально

blokant ★★
() автор топика
Ответ на: комментарий от oleg_2

там ничего особого, заново печатает про debian, т.е. как будто в буфер не записываются новые данные, при этом в сниффере ОК

blokant ★★
() автор топика
Ответ на: комментарий от oleg_2

я ведь правильно понимаю, что нужно в цикле гонять recv до тех пор, пока не поймаем нужный код возврата ? А вот если у нас есть другой протокол без таких кодов возврата и мы не получаем длину сообщения то как получить все сообщение? таймаут - единственное,что на ум приходит

blokant ★★
() автор топика
Ответ на: комментарий от blokant

Вот кусок программы.

    printf("sending HELP command\n");
    snprintf(buf, sizeof(buf), "HELP\n", pass);
    k=send(client_socket, buf, strlen(buf), 0);
    if(k<0){ printf("send()<0"); exit(0);}
    size_read = recv(client_socket, buf, sizeof(buf) - 1, 0);
    if(size_read < 0){ printf("recv()<0"); exit(0);}
    buf[size_read] = '\0';
    printf("%s",buf);
В строке
snprintf(buf, sizeof(buf), "HELP\n", pass);
ошибочно вставлена pass, хотя это незначительная ошибка.
Предлагаю распечатать возврат recv так:
    printf("sending HELP command\n");
    snprintf(buf, sizeof(buf), "HELP\n");
    k=send(client_socket, buf, strlen(buf), 0);
    if(k<0){ printf("send()<0"); exit(0);}
    size_read = recv(client_socket, buf, sizeof(buf) - 1, 0);
    printf("size_read = recv()=%d", size_read);
    if(size_read < 0){ printf("recv()<0"); exit(0);}
    buf[size_read] = '\0';
    printf("%s",buf);
И потом попробовать другие простые команды вместо HELP, типа SYST, NOP, PWD.
Может на что натолкнет. Для сравнения у меня такая выдача:
Started client with server address = 90.9.66.218
connected.
220 FTP server ready.
sending username
331 Password required for u7205
sending pass
230 User u7205 logged in
sending HELP command
214-The following commands are recognized (* =>'s unimplemented):
 CWD     XCWD    CDUP    XCUP    SMNT*   QUIT    PORT    PASV
 EPRT    EPSV    ALLO*   RNFR    RNTO    DELE    MDTM    RMD
 XRMD    MKD     XMKD    PWD     XPWD    SIZE    SYST    HELP
 NOOP    FEAT    OPTS    AUTH*   CCC*    CONF*   ENC*    MIC*
 PBSZ*   PROT*   TYPE    STRU    MODE    RETR    STOR    STOU
 APPE    REST    ABOR    USER    PASS    ACCT*   REIN*   LIST
 NLST    STAT    SITE    MLSD    MLST
214 Direct comments to root@127.0.0.1

oleg_2
()
Ответ на: комментарий от oleg_2

Попробуйте с парсером.
Распечатку посмотреть, может натолкнет на догадку.
У меня почему-то всё работает.


#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>


//   gcc my_ftp_c.c -o my_ftp_c.cgi
//   ./my_ftp_c.cgi


#define BUF_SIZE 1024
#define PORT_NUMBER 21
#define N_BYTES_MAX    1000 // максимальная длина ответа сервра

const char *username="xxxx";
const char *pass="xxxxxx";


int err0;

struct s_ftp {
    char *buf_rd;  // буфер чтения ответа сервера
    int  Sbuf_rd;  // длина буфера чтения ответа сервера
    char *p1;      // текущая позиция буфера чтения ответа
    char *p2;      // за край данных буфера чтения ответа
    int  sd1;      // сокет
    int  tm;       // тайм-аут чтения сокета
    int  n;        // счетчик байт одного ответа
    int  n_max;    // максимальное число байт одного ответа
};


//--------------------------------- read_sd ---------------------------------

int read_sd(int sd, char *buf, int Sbuf, int tm_val)
{
    fd_set rdsd;
    int k;
    struct timeval tv;

    k=read(sd, buf, Sbuf);
    if(k>=0)return(k);
    if(k<0 && errno!=EAGAIN){ err0=errno; return(-2);} //--- ошибка read ---
    FD_ZERO( &rdsd );
    FD_SET( sd, &rdsd );
    tv.tv_sec =tm_val;
    tv.tv_usec=0;
    k=select(sd+1, &rdsd, NULL, NULL, &tv );
    if(k<0){ err0=errno; return(-3);}       //--- ошибка select ---
    if(k==0){ err0=0; return(-4);}          //--- time-out ---
    k=read(sd, buf, Sbuf);
    if(k<0){ err0=errno; return(-1);}       //--- ошибка read ---
    return(k);
}

//------------------- init_s_ftp ----------------

void init_s_ftp(struct s_ftp *s, char *buf_rd, int Sbuf_rd, int n_max, int sd1, int tm)
{
    s->sd1     = sd1;
    s->tm      = tm;
    s->buf_rd  = buf_rd;
    s->Sbuf_rd = Sbuf_rd;
    s->p1      = buf_rd;
    s->p2      = buf_rd;
    s->n       = 0;
    s->n_max   = n_max;
    return;
}

//-------------------- inc_ftp ---------------------

//   Подзарядка буфера при приеме FTP-ответа

int inc_ftp(char **p1, char **p2, struct s_ftp *s)
{
    int k, n;

    if(s->n >= s->n_max) return(-8); //--- слишком длинный ответ ---
    k=read_sd(s->sd1,
              s->buf_rd,
              s->Sbuf_rd,
              s->tm);
    if(k<=0) return(-1);
    *p1 = s->buf_rd;
    *p2 = s->buf_rd + k;
    s->n = s->n + k;
    return(k);
}

//----------------------- get_str_ftp ---------------------------

// берет одну строку из сокета, если строка не входит в str, то
// строку обрезает, дочитывает до конца, но ошибку не выдает.

int get_str_ftp(struct s_ftp *s, char *str, int Sstr)
{
    char *s1, *s2; // текущая позиция str и за край str
    char *p1, *p2; // текущая позиция в buf_rd и за край данных в buf_rd
    int k;

    s1 = str;
    s2 = str + (Sstr - 1);
    p1 = s->p1;
    p2 = s->p2;
    if(p1>=p2){ k=inc_ftp(&p1, &p2, s); if(k<0) goto ERR;}
    while(*p1!='\r' && *p1!='\n'){
        *s1++ = *p1++;
        if(p1>=p2){ k=inc_ftp(&p1, &p2, s); if(k<0) goto ERR;}
        if(s1>=s2) break;
    }
    *s1=0;
    while(*p1!='\r' && *p1!='\n'){
        p1++;
        if(p1>=p2){ k=inc_ftp(&p1, &p2, s); if(k<0) goto ERR;}
    }
    if(*p1=='\r'){
        p1++; if(p1>=p2){ k=inc_ftp(&p1, &p2, s); if(k<0) goto ERR;}
    }
    if(*p1=='\n'){
        p1++;
    }
    s->p1 = p1;
    s->p2 = p2;
    return(s1 - str);

ERR:
    s->p1 = p1;
    s->p2 = p2;
    *s1=0;
    return(-1);
}

//----------------------- parser -----------------------------

int parser_ftp(struct s_ftp *s, char *buf, int Sbuf, char *val, int Sval, int *cod)
{
    int cod1=0, cod2; // коды ответов первой и текущей строки ответа
    char *p1; // текущая позиция строки str
    char *v0; // начало текущей строки буфера аргумента val
    char *v1; // текущая позиция буфера val
    char *v2; // за край буфера val
    char *b1; // текущая позиция buf
    char *b2; // за край buf
    char str[1000]; //для взятия строк ответа
    int k, n;

    buf[0]=0;
    v1 = val;
    v2 = val+(Sval - 2);
    b1 = buf;
    b2 = b1 + (Sbuf - 2);
    s->n = 0;

    k=get_str_ftp(s, str, sizeof(str));
    if(k<0) goto ERR;
    n = b2 - b1;
    if(k<n) n=k;
    memcpy(b1, str, n);
    b1 = b1 + n;
    *b1++ = '\n';
    *b1=0;
    p1=str;
    while(*p1!=0 && *p1>='0' && *p1<='9') p1++;
    cod1=atoi(str);
    if(cod1<=0 || cod1>599) goto ERR;
    if(*p1++ != '-'){  // берем аргумент однострочного ответа
        while(*p1!=0 && v1<v2) *v1++ = *p1++;
        goto RET;
    }
    while(1){  // берем аргумент многострочного ответа
        k=get_str_ftp(s, str, sizeof(str));
        if(k<0) goto ERR;
        n = b2 - b1;
        if(k<n) n=k;
        memcpy(b1, str, n);
        b1 = b1 + n;
        *b1++ = '\n';
        *b1=0;
        v0=v1;  // ставим указатель на начало текущей строки
        p1=str;
        while(*p1!=0 && *p1>='0' && *p1<='9' && v1<v2) *v1++ = *p1++;
        cod2=atoi(str);
        if(cod1==cod2 && *p1!='-'){ // признак последней строки ответа
            *v0=0; // отсекаем последнюю строку
            goto RET;
        }
        while(*p1!=0 && v1<v2) *v1++ = *p1++;
        *v1++ ='\n';
    }

RET:
    *v1=0;
    *cod=cod1;
    return(0);

ERR:
    *cod=cod1;
    *v1=0;
    return(-1);
}

//------------------- main -------------------

int main(int argc, char** argv)
{
    int k;
    int client_socket; // socket's descriptor for client
    struct sockaddr_in addr; // address of the server
    char buf[BUF_SIZE]; // buffer for incomming message
    int bytes_read; // amount of bytes
    int size_read;

    char val[2000];
    int tm=10;
    struct s_ftp s_ftp;
    char buf_rd[1024];
    int cod;


    if(argc < 2)
    {
        printf("Enter valid IP address as argument for program i.e. ftp_client 192.168.0.1 \n");
        exit(1);
    }
    printf("Started client with server address = %s\n", argv[1]);

    client_socket = socket(AF_INET, SOCK_STREAM, 0);
    if(client_socket < 0)
    {
        perror("can not create socket");
        exit(1);
    }

    addr.sin_family = AF_INET; // address family - internet socket
    addr.sin_port = htons(PORT_NUMBER);
    addr.sin_addr.s_addr = inet_addr(argv[1]);
    if(connect(client_socket,(struct sockaddr*)&addr,sizeof(addr)) < 0)
    {
        printf("Can not connect!\n");
        exit(2);
    }

    init_s_ftp(&s_ftp, buf_rd, sizeof(buf_rd), N_BYTES_MAX, client_socket, tm);

    printf("connected.\n");
    k=parser_ftp(&s_ftp, buf, sizeof(buf), val, sizeof(val), &cod);
    if(k<0){ printf("parser_ftp()<0\n"); exit(0);}
    printf("cod=%d\n",cod);
    printf("%s",buf);

    printf("sending username\n");
    snprintf(buf, sizeof(buf), "USER %s\n", username);
    k=send(client_socket, buf, strlen(buf), 0);
    if(k<0){ printf("send()<0"); exit(0);}
    k=parser_ftp(&s_ftp, buf, sizeof(buf), val, sizeof(val), &cod);
    if(k<0){ printf("parser_ftp()<0\n"); exit(0);}
    printf("cod=%d\n",cod);
    printf("%s",buf);

    printf("sending pass\n");
    snprintf(buf, sizeof(buf), "PASS %s\n", pass);
    k=send(client_socket, buf, strlen(buf), 0);
    if(k<0){ printf("send()<0"); exit(0);}
    k=parser_ftp(&s_ftp, buf, sizeof(buf), val, sizeof(val), &cod);
    if(k<0){ printf("parser_ftp()<0\n"); exit(0);}
    printf("cod=%d\n",cod);
    printf("%s",buf);

    printf("sending HELP command\n");
    snprintf(buf, sizeof(buf), "HELP\n", pass);
    k=send(client_socket, buf, strlen(buf), 0);
    if(k<0){ printf("send()<0"); exit(0);}
    k=parser_ftp(&s_ftp, buf, sizeof(buf), val, sizeof(val), &cod);
    if(k<0){ printf("parser_ftp()<0\n"); exit(0);}
    printf("cod=%d\n",cod);
    printf("%s",buf);

    printf("sending PASV command\n");
    snprintf(buf, sizeof(buf), "PASV\n", pass);
    k=send(client_socket, buf, strlen(buf), 0);
    if(k<0){ printf("send()<0"); exit(0);}
    k=parser_ftp(&s_ftp, buf, sizeof(buf), val, sizeof(val), &cod);
    if(k<0){ printf("parser_ftp()<0\n"); exit(0);}
    printf("cod=%d\n",cod);
    printf("%s",buf);

    return 0;
}
oleg_2
()
Ответ на: комментарий от oleg_2

Заметил ошибку в последней программе, но не знаю, как исправить.

Написано:

    if(*p1++ != '-'){  // берем аргумент однострочного ответа
        while(*p1!=0 && v1<v2) *v1++ = *p1++;
        goto RET;
    }
Следует быть:
    if(*p1 != '-'){  // берем аргумент однострочного ответа
        if(*p1!=0) p1++;
        while(*p1!=0 && v1<v2) *v1++ = *p1++;
        goto RET;
    }

oleg_2
()
Ответ на: комментарий от oleg_2

использование goto наводит на мысль, что вы что-то делаете не так, теперь работает, но все же буду разбирать свое. Спасибо

blokant ★★
() автор топика
Ответ на: комментарий от oleg_2

да и вот вы, например, recv не делаете в цикле(тогда когда приходит большой объем текста про debian) и получаете его в следующих recv, когда уже работаете с паролем и с командами, что не есть хорошо, я цикл использовал как раз для этих целей

blokant ★★
() автор топика
Ответ на: комментарий от blokant

Я так понял, что стало работать?

«использование goto наводит на мысль, что вы что-то делаете не так»
Так и не считайте, что это образец написания. Я знаю, что есть нелюбители goto.

Вы бы показали распечатку, ту где не работало последний раз.

Я не заметил это сообщение:
«я ведь правильно понимаю, что нужно в цикле гонять recv до тех пор,
пока не поймаем нужный код возврата ? А вот если у нас есть другой
протокол без таких кодов возврата и мы не получаем длину сообщения
то как получить все сообщение? таймаут - единственное,что на ум приходит»

FTP-ответ всегда начинается кодом ответа (его и надо выпарсить).
Ответ всегда состоит из одной или нескольких строк, т. е. заканчивается '\n'.
Если строка начинается с кода, и за кодом нет тире, то это последняя строка
ответа. Например:

257 "/usr/home/test3test/client_ftp" is the current directory
это первая и последняя строка ответа.

Другой пример:

230- 
230- The programs included with the Debian GNU/Linux system are free software;
230- the exact distribution terms for each program are described in the
230- individual files in /usr/share/doc/*/copyright.
230- 
230- Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
230- permitted by applicable law.
230 User nexus logged in.
Весь этот ответ начинается с кода (первая строка), а последняя строка тоже начинается
с этого же кода, но тире нет - значит последняя. И всё это один-единственный ответ.

Последняя программа с парсером, который умеет брать ровно один ответ целиком.
Вы можете написать парсер по своему, но он должен уметь то же самое.

Наверняка и в других протоколах есть правила, как определить конец сообщения.
Но только не тайм-аут. По тайм-ауту считается ошибка - сервер завис.

==============================================

«да и вот вы, например, recv не делаете в цикле»

Как это не делаю? Делаю.

int parser_ftp() // парсер выбирает весь ответ, однострочный или многострочный
{
      ...
    k=get_str_ftp(s, str, sizeof(str)); // берем строку из сокета
      ...
}

int get_str_ftp() // всегда выдает цельную строку, хотя бы пришедшую по кускам
{
      ...
    if(p1>=p2){ k=inc_ftp(&p1, &p2, s); if(k<0) goto ERR;} // подзарядка буфера
      ...
}

int inc_ftp() // подзаряжает буфер чтения
{
      ...
    k=read_sd()
      ...
}
Функция get_str_ftp вызывает recv (у меня read) столько раз, сколько нужно для
получения всей строки, т. е. пока не получит '\n'.
Парсер посредством get_str_ftp берет столько строк, пока не получит последнюю
строку ответа, а именно ту, в которой есть код, и он совпадает с кодом первой
строки, но без тире.

Для полной уверенности предлагаю Вам провести такой эксперимент.
Чтение сокета в функции read_sd, там есть две одинаковых строки:
k=read(sd, buf, Sbuf);
Замените их обе на такую строку:
k=read(sd, buf, 1);
чтобы читало по одному байту.
И попробуйте.
Можно по другому - задайте в функции main размер буфера buf_rd 1 байт.

oleg_2
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.