NoMan
NoMan

Reputation: 401

Server tries to accept a connection and fail with "Socket operation on non-socket" ever though there is no connect operation from the client

I am doing a very simple application using TCP client-server protocol. First I set up the socket for server and start listening to the client requests. After the connection is established the client sends me a file and I read the file but after I close the connection another accept() call happen on the server-side and it fails. I am only doing connect() once on the client side. And I see the socket descriptor value on the second accept is different from the first one.

Thank you.

EDIT: It seems like I'm overwriting the value of the socket descriptor with the call to Readline() function in utility.c. My guess is that some lin in function might have overwritten the value above its stack.

server.c

#include <sys/socket.h>       /*  socket definitions        */
#include <sys/types.h>        /*  socket types              */
#include <arpa/inet.h>        /*  inet (3) funtions         */
#include <errno.h>
#include <unistd.h>           /*  misc. UNIX functions      */
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <strings.h>

#include "utility.h"           /*  our own utility functions  */


/*  Global constants  */

#define ECHO_PORT          (2002)
#define MAX_LINE           (1000)


int main(int argc, char *argv[]) {
    int       list_s;                /*  listening socket          */
    int       conn_s;                /*  connection socket         */
    short int port;                  /*  port number               */
    struct    sockaddr_in servaddr;  /*  socket address structure  */
    struct    sockaddr_in incoming_addr;
    char*     endptr;                /*  for strtol()              */


    /*  Get port number from the command line, and
        set to default port if no arguments were supplied  */

    if ( argc == 2 ) {
    port = strtol(argv[1], &endptr, 0);
    if ( *endptr ) {
        fprintf(stderr, "ECHOSERV: Invalid port number.\n");
        exit(EXIT_FAILURE);
    }
    }
    else if ( argc < 2 ) {
    port = ECHO_PORT;
    }
    else {
    fprintf(stderr, "ECHOSERV: Invalid arguments.\n");
    exit(EXIT_FAILURE);
    }


    /*  Create the listening socket  */

    list_s = socket(AF_INET, SOCK_STREAM, 0);
    if (list_s < 0) {
    fprintf(stderr, "ECHOSERV: Error creating listening socket.\n");
    exit(EXIT_FAILURE);
    }
    else {
        fprintf(stdout, "Socket successfully created.\n");
    }


    /*  Set all bytes in socket address structure to
        zero, and fill in the relevant data members   */

    memset(&servaddr, 0, sizeof(servaddr));
    servaddr.sin_family      = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_port        = htons(port);


    /* Bind the socket descriptor "list_s" to the servaddr which defines the port number. */

    if ( bind(list_s, (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0 ) {
    fprintf(stderr, "ECHOSERV: Error calling bind()\n");
    exit(EXIT_FAILURE);
    }
    else {
        fprintf(stdout, "Socket successfully binded to port no %d.\n", port);
    }

    if ( listen(list_s, 1) < 0 ) {
    fprintf(stderr, "ECHOSERV: Error calling listen()\n");
    exit(EXIT_FAILURE);
    }
    else {
        fprintf(stdout, "Now listening to port: %d.\n", port);
    }


    /*  Enter an infinite loop to respond
        to client requests and echo input  */

    while (1) {
    /*  Wait for a connection, then accept() it  */
        socklen_t incoming_addr_len = sizeof(incoming_addr);
        int server_socket = list_s;
        printf("The value of list_s is: %d.\n", list_s);
        conn_s = accept(server_socket, (struct sockaddr*)(&incoming_addr), &incoming_addr_len);
    if ( conn_s < 0 ) {
        fprintf(stderr, "ECHOSERV: Error calling accept(): %s.\n", strerror(errno));
        exit(EXIT_FAILURE);
    }

        /* Read the data from the socket descriptor which in this case  */
        /* is the size of the file that is to be received. */

        int data_size;
        Readline(conn_s, &data_size, sizeof(data_size));


        /* Read data from the given socket descriptor conn_s and then
         * write the same data to the descriptor again to be sent as
         * a response. */

        char buffer[data_size];
        Readline(conn_s, buffer, data_size);
        printFile(buffer, data_size);


    /*  Close the connected socket  */

    if ( close(conn_s) < 0 ) {
        fprintf(stderr, "ECHOSERV: Error calling close()\n");
        exit(EXIT_FAILURE);
    }
    }
}

Client.c

#include <sys/socket.h>       /*  socket definitions        */
#include <sys/types.h>        /*  socket types              */
#include <arpa/inet.h>        /*  inet (3) funtions         */
#include <unistd.h>           /*  misc. UNIX functions      */
#include <stdlib.h>
#include <string.h>
#include <stdio.h>

#include "utility.h"           /*  Our own utility functions  */


/*  Global constants  */

#define MAX_LINE           (1000)
#define DATA_IN_FILE       (4)

static const char* const kFileName = "/home/pb/Desktop/workspace/Networking/practice_project/practice_project_test_file_1";

/*  Function declarations  */

int ParseCmdLine(int argc, char *argv[], char **szAddress, char **szPort);


/*  main()  */

int main(int argc, char *argv[]) {

    int       conn_s;                /*  connection socket         */
    short int port;                  /*  port number               */
    struct    sockaddr_in servaddr;  /*  socket address structure  */
    char      buffer[MAX_LINE];      /*  character buffer          */
    char     *szAddress;             /*  Holds remote IP address   */
    char     *szPort;                /*  Holds remote port         */
    char     *endptr;                /*  for strtol()              */


    /*  Get command line arguments  */

    ParseCmdLine(argc, argv, &szAddress, &szPort);


    /*  Set the remote port  */

    port = strtol(szPort, &endptr, 0);
    if ( *endptr ) {
    printf("ECHOCLNT: Invalid port supplied.\n");
    exit(EXIT_FAILURE);
    }


    /*  Create the listening socket  */

    if ( (conn_s = socket(AF_INET, SOCK_STREAM, 0)) < 0 ) {
    fprintf(stderr, "ECHOCLNT: Error creating listening socket.\n");
    exit(EXIT_FAILURE);
    }


    /*  Set all bytes in socket address structure to
        zero, and fill in the relevant data members   */

    memset(&servaddr, 0, sizeof(servaddr));
    servaddr.sin_family      = AF_INET;
    servaddr.sin_port        = htons(port);


    /*  Set the remote IP address  */

    if ( inet_aton(szAddress, &servaddr.sin_addr) <= 0 ) {
    printf("ECHOCLNT: Invalid remote IP address.\n");
    exit(EXIT_FAILURE);
    }


    /*  connect() to the remote echo server  */
    if ( connect(conn_s, (struct sockaddr *) &servaddr, sizeof(servaddr) ) < 0 ) {
    printf("ECHOCLNT: Error calling connect()\n");
    exit(EXIT_FAILURE);
    }

    FILE* f = fopen(kFileName, "rb");
    if (f == NULL) {
        perror("Error ");
        return -1;
    }
    fseek(f, 0, SEEK_END);
    int size_of_file = ftell(f);
    rewind(f);
    char file_buffer[size_of_file];
    int items_read = fread(file_buffer, 1, size_of_file, f);
    if (items_read != size_of_file) {
        printf("Read the rest.\n");
    }

    /* First sending to the server the size of the data in the  */
    /* file in bytes as an integer. */

    sendData(conn_s, &size_of_file, sizeof(int));

    /* This is the actual file now that we have let the server  */
    /* know the size of the file to be send. */
    sendData(conn_s, file_buffer, size_of_file);

    return EXIT_SUCCESS;
}


int ParseCmdLine(int argc, char *argv[], char **szAddress, char **szPort) {

    int n = 1;

    while ( n < argc ) {
    if ( !strncmp(argv[n], "-a", 2) || !strncmp(argv[n], "-A", 2) ) {
        *szAddress = argv[++n];
    }
    else if ( !strncmp(argv[n], "-p", 2) || !strncmp(argv[n], "-P", 2) ) {
        *szPort = argv[++n];
    }
    else if ( !strncmp(argv[n], "-h", 2) || !strncmp(argv[n], "-H", 2) ) {
        printf("Usage:\n\n");
        printf("    timeclnt -a (remote IP) -p (remote port)\n\n");
        exit(EXIT_SUCCESS);
    }
    ++n;
    }

    return 0;
}

utility.c

#include "utility.h"

#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <stdint.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <unistd.h>
#include <errno.h>


int printFile(char *binary_buffer, int buffer_size) {
    int current_position = 0;
    while (current_position < buffer_size) {
        if(binary_buffer[current_position] == 0) {
            printf("\n");
            printf("Type 1.\n");
            current_position = read_first_type(binary_buffer, current_position);
        }
        else if(binary_buffer[current_position] == 1) {
            printf("\n");
            printf("Type 2.\n");
            current_position = read_second_type(binary_buffer, current_position);
        }
    }
    return 0;
}


int read_first_type(const char* binary, const int offset) {
    int size_of_units = 2;
    int no_of_units;
    short unit_buffer[1];

    // current position is always Type and no_of_units is amount which is a byte off of Type.
    no_of_units = binary[offset+1]; 
    printf("No of units is %d.\n", no_of_units);
    for (int i=0; i<no_of_units; ++i) {
        // This is a Big-endian system so the values will bit off.
        memcpy(unit_buffer, (binary+offset+2)+i*size_of_units, 2);
        printf("Type 1 unit has number: %d.\n", unit_buffer[0]);
    }
    return (offset + no_of_units*size_of_units + 2); 
}


int read_second_type(const char* binary, const int offset) {
    int no_of_units;
    int current_position = 4;
    char amount[4];
    memcpy(amount, binary+offset+1, 3);
    amount[3] = '\0';
    no_of_units = atoi(amount);
    printf("No of units is %d.\n", no_of_units);
    char unit_buffer[5];
    int count;
    for(int i=0; i<no_of_units; ++i) {
        count = 0;
        while(1) {
            memcpy(unit_buffer+count, binary+offset+current_position+count, 1);
            if (unit_buffer[count] == 44) {
                unit_buffer[count] = '\0';
                current_position += count+1;
                break;
            }
            if (unit_buffer[count] == 0 || unit_buffer[count] == 1) {
                unit_buffer[count] = '\0';
                printf("Type 2 unit has number: %d.\n",atoi(unit_buffer));
                return (offset+current_position+count);
            }
            count++;
        }
        printf("Type 2 unit has number: %d.\n",atoi(unit_buffer));
    }
    return offset+current_position+count;
}

/*  Read a line from a socket  */

ssize_t Readline(int sockd, void *vptr, size_t maxlen) {
    ssize_t n, rc;
    char    c, *buffer;

    buffer = vptr;

    for ( n = 0; n < maxlen; n++ ) {
    if ( (rc = read(sockd, &c, 1)) == 1 ) {
        *buffer++ = c;
        if ( c == '\n' )
        break;
    }
    else if ( rc == 0 ) {
        if ( n == 0 )
        return 0;
        else
        break;
    }
    else {
        if ( errno == EINTR )
        continue;
        return -1;
    }
    }

    *buffer = 0;
    return n;
}


/*  Write a line to a socket  */

ssize_t sendData(int sockd, const void *vptr, size_t n) {
    size_t      nleft;
    ssize_t     nwritten;
    const char *buffer;

    buffer = vptr;
    nleft  = n;

    while ( nleft > 0 ) {
    if ( (nwritten = write(sockd, buffer, nleft)) <= 0 ) {
        if ( errno == EINTR )
        nwritten = 0;
        else
        return -1;
    }
    nleft  -= nwritten;
    buffer += nwritten;
    }

    return n;
}

Upvotes: 0

Views: 222

Answers (1)

Steffen Ullrich
Steffen Ullrich

Reputation: 123260

If the full buffer was read in ReadLine (i.e. maxlen reached in loop) then you will still write a 0x0 after the last character read, thus causing an out of buffer:

ssize_t Readline(int sockd, void *vptr, size_t maxlen) {
 ...
    for ( n = 0; n < maxlen; n++ ) {
        if ( (rc = read(sockd, &c, 1)) == 1 ) {
           *buffer++ = c;
 ...
    *buffer = 0;         <<<<<<<<<<<< might be vptr[maxlen+1], i.e. buffer overflow
    return n;

And this will almost always occur when doing

  Readline(conn_s, &data_size, sizeof(data_size));

Only if the octets making up data_size contain an \n before the end of the buffer it will not occur but then you will read the wrong number.

This buffer overflow might cause a corruption of adjacent memory which might lead to the problem you see.

Upvotes: 2

Related Questions