Partial write() detected from application on a blocking tcp socket

Solution Verified - Updated -

Issue

A multi-threaded application detects partial write on a blocking tcp socket.
The multi-threaded application checks the return value of the write() function in one of its "sending" threads.

$man 2 write
...
SYNOPSIS
#include <unistd.h>

ssize_t write(int fd, const void *buf, size_t count);
...
RETURN VALUE
On success, the number of bytes written is returned (zero indicates nothing was written).
On error, -1 is returned, and errno is set appropriately.
...

Application has a condition that if the return value from write() does not equal to the count value, it is considered as an error.

Sample C++ application code
Client program
This application is a simple tcp client consuming all data sent by the server.
client.cxx:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <signal.h>
#include <time.h>
#include <fcntl.h>
#include <errno.h>
#include <syslog.h>
#include <string.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <iostream>
using namespace std;

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

    #define ERR_BUFF_SIZE sizeof(err_buff)
    #define CLI_ERR errno<<" "<<strerror_r(errno, err_buff, ERR_BUFF_SIZE)

    if(argc < 3) {
        std::cerr<<"Usage client <server_ip> <server_port>"<<std::endl;
        return -1;
    }

    int cli_fd;
    if((cli_fd = ::socket(AF_INET, SOCK_STREAM, 0)) == -1) {
        std::cerr<<"socket() failed, error: "<<CLI_ERR<<std::endl;
        return -1;
    }

    sockaddr_in server_address;
    memset(&server_address, 0, sizeof(server_address));

    std::cout<<"Connecting to server: "<<argv[1]<<":"<<argv[2]<<std::endl;

    server_address.sin_family = AF_INET;
    server_address.sin_port = htons(atoi(argv[2]));

    int ret;
    if((ret = ::inet_pton(AF_INET, (const char*)argv[1], &server_address.sin_addr)) == -1) {
        std::cerr<<"inet_pton() failed, error: "<<CLI_ERR<<std::endl;
    } else if(ret == 0) {
        std::cerr<<"inet_pton() failed, error: srv address invalid"<<std::endl;
    }

    if(ret <= 0)
        return -1;

    int connect_result;
    if(::connect(cli_fd, (sockaddr*)&server_address, sizeof(server_address)) == -1) {
        std::cerr<<"connect() failed: error: "<<CLI_ERR<<std::endl;
        return -1;
    }

    std::cout<<"Connected to server: "<<argv[1]<<":"<<argv[2]<<std::endl;

    char rcv_buff[1000];
    while(1) {
        if(::read(cli_fd, rcv_buff, sizeof(rcv_buff)) == -1) {
            std::cerr<<"read() failed, error: "<<CLI_ERR<<std::endl;
            break;
        }
        usleep(10);
    }
    close(cli_fd);
    return 0;
}

Server program
This is a multi-threaded tcp server application consisting of three threads:

  1. main application thread - spawning others
  2. sender thread which is responsible for pumping data towards the connected client every 100us.
  3. permission thread which is responsible for acquiring and releasing root privileges every 500000us.

server.cxx:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <signal.h>
#include <time.h>
#include <fcntl.h>
#include <errno.h>
#include <syslog.h>
#include <string.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <iostream>

using namespace std;

void* set_perm(void *args);
void* traffic_send(void* args);
int port;

/* Main application thread */
int main(int args, char* argv[]) {
    pthread_t sender_t;
    pthread_t permission_t; 

    #define ERR_BUFF_SIZE sizeof(err_buff)
    #define SRV_ERR_R errno<<" "<<strerror_r(errno, err_buff, ERR_BUFF_SIZE)
    #define SRV_ERR err<<" "<<strerror(err)

    if(args < 2) {
         cout<<"Usage: server <port>"<<std::endl;
         return 0;
    }

    port = atoi(argv[1]);
    std::cout<<"Port: "<<port<<std::endl;

    int err;
    if((err = pthread_create(&sender_t, NULL, &traffic_send, NULL)) != 0) {
        std::cerr<<"pthread_create() failed - sender thread, error: "<<SRV_ERR<<std::endl;      
        return -1;
    }

    if((err = pthread_create(&permission_t, NULL, &set_perm, NULL)) != 0) {
        std::cerr<<"pthread_create() failed - permission thread, error: "<<SRV_ERR<<std::endl;      
        return -1;
    }

    pthread_join(sender_t, NULL);                         
    pthread_join(permission_t, NULL);
    return 0;
}

/* sender thread  */
void* traffic_send(void* args) {
    int listner_fd;
    char err_buff[200];

    if((listner_fd = ::socket(AF_INET, SOCK_STREAM, 0)) == -1) {
        std::cerr<<"socket() failed, error: "<<SRV_ERR_R<<std::endl;
        return NULL;
    }

    const int optval = 1;
    if(::setsockopt(listner_fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)) == -1) {
        std::cerr<<"setsockopt() failed, socket: "<<listner_fd<<", error: "<<SRV_ERR_R<<std::endl;
        return NULL;
    }

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

    if(::bind(listner_fd, (const sockaddr*)&server_address, sizeof(sockaddr_in)) == -1) {
        std::cerr<<"bind() failed, port: "<<port<<", error: "<<SRV_ERR_R<<std::endl;
        return NULL;
    }

    if(::listen(listner_fd, 1) == -1) {
        std::cerr<<"listen() failed, port: "<<port<<", error: "<<SRV_ERR_R<<std::endl;
        return NULL;
    }

    /* server only one client */
    int connection_fd;
    if((connection_fd = ::accept(listner_fd, (sockaddr*)NULL, (socklen_t*)NULL)) == -1) {
        std::cerr<<"accept() failed, port: "<<port<<", error: "<<SRV_ERR_R<<std::endl;
        return NULL;
    }

    char snd_buff[100000];
    #define SND_BUFF_SIZE sizeof(snd_buff)

    memset(snd_buff, 'x', SND_BUFF_SIZE);
    long total_data = 0;
    ssize_t written;

    std::cout<<"Traffic thread started: 100000 bytes every 100us"<<std::endl;
    for(int i=0; i< 1000; i++){
        if((written = write(connection_fd, snd_buff, SND_BUFF_SIZE)) != SND_BUFF_SIZE) 
            std::cout<<"write() failed, written: "<<written<<", expected: "<<SND_BUFF_SIZE<<", error: "<<SRV_ERR_R<<std::endl;
        usleep(100);
        total_data += written;
    }
    std::cout<<"Traffic thread ended"<<std::endl;
    std::cout<<"Total data sent = "<<total_data<<std::endl;
    ::shutdown(connection_fd, SHUT_RDWR);
    ::close(connection_fd);
}

/* permission thread */
void* set_perm(void *args) {
    char err_buff[200];

    std::cout<<"Permission thread started: Permission acquired every 500000us"<<std::endl;
    for(int i = 0; i < 20; ++i)
    {
        uid_t user_id = getuid();
        if(setuid(geteuid()) == -1)
        {
            std::cerr<<"setuid() failed, error: "<<SRV_ERR_R<<std::endl;
            return NULL;
        }

        usleep(500000);

        if(setreuid(user_id, 0) == -1){
            std::cerr<<"setreuid() failed, error: "<<SRV_ERR_R<<std::endl;
            return NULL;
        }
    }
    std::cout<<"Permission thread ended"<<std::endl;
}

Compilation

$g++ client.cxx -o client
$g++ server.cxx -o server -lpthread
$ls -l ./server
-rwsr-x---. 1 root xxxx Nov 7 23:29 server

Execution
$server <port>

$./server 10000
Port: 10000
Permission thread started: Permission acquired every 500000us
Traffic thread started: 100000 bytes every 100us
write() failed, written: 48544, expected: 100000, error: 0 Success
write() failed, written: 1056, expected: 100000, error: 0 Success
write() failed, written: 66592, expected: 100000, error: 0 Success
write() failed, written: 33824, expected: 100000, error: 0 Success
write() failed, written: 50208, expected: 100000, error: 0 Success
write() failed, written: 33824, expected: 100000, error: 0 Success
write() failed, written: 50208, expected: 100000, error: 0 Success
write() failed, written: 17440, expected: 100000, error: 0 Success
write() failed, written: 50208, expected: 100000, error: 0 Success
write() failed, written: 82976, expected: 100000, error: 0 Success
write() failed, written: 66592, expected: 100000, error: 0 Success
write() failed, written: 66592, expected: 100000, error: 0 Success
write() failed, written: 50208, expected: 100000, error: 0 Success
write() failed, written: 66592, expected: 100000, error: 0 Success
write() failed, written: 50208, expected: 100000, error: 0 Success
Traffic thread ended
Total data sent = 99235072
Permission thread ended

$client <server_ip> <server_port>

$./client 127.0.0.1 10000
Connecting to server: 127.0.0.1:10000
Connected to server: 127.0.0.1:10000
^C

Not all data has been sent from the server to the client!

Environment

Red Hat Enterprise Linux (RHEL), all versions

Subscriber exclusive content

A Red Hat subscription provides unlimited access to our knowledgebase, tools, and much more.

Current Customers and Partners

Log in for full access

Log In

New to Red Hat?

Learn more about Red Hat subscriptions

Using a Red Hat product through a public cloud?

How to access this content