Partial write() detected from application on a blocking tcp socket
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:
- main application thread - spawning others
- sender thread which is responsible for pumping data towards the connected client every 100us.
- 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.