выполнение команд через сокеты с помощью popen ()

Может кто-нибудь дать мне руку, пытающуюся реализовать следующий сервер и клиент ?:

Сервер:

#include  #include  #include  #include  #include  #include  int main(void) { int sock = socket(AF_INET, SOCK_STREAM, 0); struct sockaddr_in serv_addr = { 0 }; serv_addr.sin_family = AF_INET; serv_addr.sin_addr.s_addr = INADDR_ANY; serv_addr.sin_port = htons(1234); bind(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)); listen(sock, 128); struct sockaddr_in cli_addr = { 0 }; socklen_t cli_addrlen = sizeof(cli_addr); int acc_sock = accept(sock, (struct sockaddr *)&cli_addr, &cli_addrlen); printf("[+] Connected \n"); char buf[1024]; ssize_t nread; memset(buf, 0, sizeof(buf)); int a; while (1) { nread = read(0, buf, 1024); write(acc_sock, buf, nread); memset(buf, 0, sizeof(buf)); while ((read(acc_sock, buf, 1024)) != 0) { printf("%s", buf); memset(buf, 0, sizeof(buf)); } } } 

Все серверы – это сканирование команды из stdin и отправка ее клиенту через сокеты. Затем сканирует ответ клиента и выводит его на стандартный stdout .

Клиент:

 #include  #include  #include  #include  #include  #include  int main(int argc, const char *argv[]) { int sock = socket(AF_INET, SOCK_STREAM, 0); struct sockaddr_in serv = { 0 }; char buf[1024]; char command[1024]; memset(buf, 0, sizeof(buf)); memset(command, 0, sizeof(command)); int nread; FILE *in; extern FILE *popen(); serv.sin_family = AF_INET; serv.sin_port = htons(atoi(argv[1])); serv.sin_addr.s_addr = inet_addr("127.0.0.1"); int a; connect(sock, (struct sockaddr*)&serv, sizeof(serv)); while (1) { nread = read(sock, buf, 1024); in = popen(buf, "r"); while ((fgets(command, sizeof(command), in)) != NULL) {; write(sock, command, sizeof(command)); } memset(buf, 0, sizeof(buf)); } return 0; } 

По сути клиент получает команду, проверенную сервером, выполняет ее с помощью popen() и отправляет содержимое командной строки по строке до NULL .

Проблема в том, что код внезапно перестает работать сразу после первой команды. передача команды и выход команды удовлетворительна, однако после печати вывода первой команды программа просто перестает работать. Я думаю, это проблема с fgets() , однако я могу ошибаться. Есть ли решения для этой проблемы?

Предостережение. Это может (или не может) соответствовать вашим потребностям, потому что я изменил смысл кликов и циклов сервера в исправленном коде ниже. Как я уже упоминал в своем комментарии выше:

Обычная ориентация для приложения, подобного этому, заключается в том, что клиент подключается к серверу, а клиент передает команды [read from stdin ] на сервер [который делает popen ] и возвращает результаты. Вот как работает ssh . Ваша ориентация меняется на противоположную. То, что у вас есть, это огонь sshd и ждать ssh для подключения, а затем sshd отправляет команды в ssh . Другими словами, петли на соответствующих сторонах должны переключаться.

Реверсирование этого было единственным способом для меня. Если разворот не работает [хорошо] для вашего желаемого варианта использования, приведенный ниже код может все же дать вам некоторые идеи.

Я решил проблему зависания, представив концепцию символа флага для обозначения конца вывода. Я взял эту концепцию из протокола PPP [точка-точка] по RS-232 .

Символом флага является только заданное значение (например, 0x10 ), которое вряд ли будет частью обычных данных. Поскольку ваши данные, скорее всего, ascii или utf-8 , могут использоваться любые [неиспользуемые] символы в диапазоне 0x00-0x1F (т. 0x00-0x1F Не использовать вкладку, cr, новую строку и т. Д.).

Если вам нужно передать символ флага (т. 0x00-0xFF Ваши данные должны быть полным двоичным диапазоном 0x00-0xFF ), я включил некоторые процедуры кодирования пакетов / декодирования, которые реализуют escape-коды, используемые в PPP выше. Я закодировал их, но на самом деле их не зацепил. В этом случае символы флага [и escape] могут быть любыми двоичными значениями [обычно 0xFF и 0xFE соответственно].

Для простоты я объединил обе стороны в один файл .c . Вызовите сервер с -s [first].

Во всяком случае, вот протестированный код [пожалуйста, помилование бесплатной очистки стилей]:

 // inetpair/inetpair -- server/client communication #include  #include  #include  #include  #include  #include  #include  typedef unsigned char byte; #define BUFMAX 1024 int port = 1234; int opt_svr; int opt_debug; #define FLAG 0x10 #define ESC 0x11 #define ESC_FLAG 0x01 #define ESC_ESC 0x02 #define dbgprt(_fmt...) \ do { \ if (opt_debug) \ printf(_fmt); \ } while (0) // client int client(void) { int sock; struct sockaddr_in serv = { 0 }; char *cp; char buf[BUFMAX + 1]; int nread; int flag; int exitflg; sock = socket(AF_INET, SOCK_STREAM, 0); serv.sin_family = AF_INET; serv.sin_port = htons(port); serv.sin_addr.s_addr = inet_addr("127.0.0.1"); connect(sock, (struct sockaddr *) &serv, sizeof(serv)); while (1) { cp = fgets(buf,BUFMAX,stdin); if (cp == NULL) break; exitflg = (strcmp(buf,"exit\n") == 0); // send the command nread = strlen(buf); write(sock, buf, nread); if (exitflg) break; while (1) { dbgprt("client: PREREAD\n"); nread = read(sock, buf, 1024); dbgprt("client: POSTREAD nread=%d\n",nread); if (nread <= 0) break; cp = memchr(buf,FLAG,nread); flag = (cp != NULL); if (flag) nread = cp - buf; write(1,buf,nread); if (flag) break; } } close(sock); return 0; } // server int server(void) { struct sockaddr_in serv_addr = { 0 }; int sock; int acc_sock; char buf[BUFMAX + 1]; char command[BUFMAX + 1]; ssize_t nread; FILE *pin; FILE *xfin; char *cp; struct sockaddr_in cli_addr = { 0 }; opt_debug = ! opt_debug; dbgprt("[+] Starting\n"); sock = socket(AF_INET, SOCK_STREAM, 0); serv_addr.sin_family = AF_INET; serv_addr.sin_addr.s_addr = INADDR_ANY; serv_addr.sin_port = htons(port); bind(sock, (struct sockaddr *) &serv_addr, sizeof(serv_addr)); listen(sock, 128); while (1) { socklen_t cli_addrlen = sizeof(cli_addr); dbgprt("[+] Waiting for connection\n"); acc_sock = accept(sock,(struct sockaddr *)&cli_addr,&cli_addrlen); dbgprt("[+] Connected\n"); xfin = fdopen(acc_sock,"r"); while (1) { dbgprt("[+] Waiting for command\n"); cp = fgets(buf,BUFMAX,xfin); if (cp == NULL) break; cp = strchr(buf,'\n'); if (cp != NULL) *cp = 0; dbgprt("[+] Command '%s'\n",buf); if (strcmp(buf,"exit") == 0) break; pin = popen(buf, "r"); while (1) { cp = fgets(command, BUFMAX, pin); if (cp == NULL) break; nread = strlen(command); write(acc_sock, command, nread); } pclose(pin); command[0] = FLAG; write(acc_sock,command,1); } fclose(xfin); close(acc_sock); dbgprt("[+] Disconnect\n"); } } // packet_encode -- encode packet // RETURNS: (outlen << 1) int packet_encode(void *dst,const void *src,int srclen) { const byte *sp = src; byte *dp = dst; const byte *ep; byte chr; int dstlen; // encode packet in manner similar to PPP (point-to-point) protocol does // over RS-232 line ep = sp + srclen; for (; sp < ep; ++sp) { chr = *sp; switch (chr) { case FLAG: *dp++ = ESC; *dp++ = ESC_FLAG; break; case ESC: *dp++ = ESC; *dp++ = ESC_ESC; break; default: *dp++ = chr; break; } } dstlen = dp - (byte *) dst; dstlen <<= 1; return dstlen; } // packet_decode -- decode packet // RETURNS: (outlen << 1) | flag int packet_decode(void *dst,const void *src,int srclen) { const byte *sp = src; byte *dp = dst; const byte *ep; byte chr; int flag; int dstlen; // decode packet in manner similar to PPP (point-to-point) protocol does // over RS-232 line ep = sp + srclen; flag = 0; while (sp < ep) { chr = *sp++; flag = (chr == FLAG); if (flag) break; switch (chr) { case ESC: chr = *sp++; switch (chr) { case ESC_FLAG: *dp++ = FLAG; break; case ESC_ESC: *dp++ = ESC; break; } break; default: *dp++ = chr; break; } } dstlen = dp - (byte *) dst; dstlen <<= 1; if (flag) dstlen |= 0x01; return dstlen; } int main(int argc, char **argv) { char *cp; --argc; ++argv; for (; argc > 0; --argc, ++argv) { cp = *argv; if (*cp != '-') break; switch (cp[1]) { case 'd': opt_debug = 1; break; case 'P': port = atoi(cp + 2); break; case 's': opt_svr = 1; break; } } if (opt_svr) server(); else client(); return 0; } 

Клиент никогда не закрывает sock . Поэтому цикл сервера

  while ((read(acc_sock, buf, 1024)) != 0) { printf("%s", buf); memset(buf, 0, sizeof(buf)); } 

никогда не заканчивается. Вам нужен какой-то механизм для информирования сервера о том, что все выходные данные команды были отправлены. Может быть, что-то похожее на кодирование передачи HTTP с чередованием.