Bug 14500 - socket data is truncated for data length ~ 4-5 K
Summary: socket data is truncated for data length ~ 4-5 K
Status: RESOLVED INVALID
Alias: None
Product: glibc
Classification: Unclassified
Component: libc (show other bugs)
Version: 2.16
: P2 normal
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2012-08-20 22:53 UTC by herefishy
Modified: 2014-06-17 05:57 UTC (History)
2 users (show)

See Also:
Host:
Target:
Build:
Last reconfirmed:
fweimer: security-


Attachments
Socket server, returns a static string of about 5.5K (1.38 KB, text/x-csrc)
2012-08-20 22:53 UTC, herefishy
Details
C client to test the server (833 bytes, text/x-csrc)
2012-08-20 22:57 UTC, herefishy
Details
php client also can be used to test the server (559 bytes, application/x-php)
2012-08-20 22:58 UTC, herefishy
Details

Note You need to log in before you can comment on or make changes to this bug.
Description herefishy 2012-08-20 22:53:18 UTC
Created attachment 6594 [details]
Socket server, returns a static string of about 5.5K

C socket server on remote host attempts to send back ~ 4 - 5 kbytes of data to php or C client, data gets truncated and an error is generated. Works works fairly well if the Server is on the LAN. Works OK if the data length is shorter, around 400 bytes. 

I have included C and php clients and C server code complete. 

The workaround is to put a sleep(1) before closing the socket in the child thread. (search for sleep in the server code below). Failing to close the socket in the parent results in > 90% CPU after a few thousand requests. 

glibc-2.16.0 gcc-4.7.1
gcc-4.7.1 Target: i686-pc-linux-gnu 
GNU ld (GNU Binutils) 2.22

kernel build, target centos  2.6.18-164.el5 #1 SMP Thu Sep 3 03:33:56 EDT 2009 i686 i686 i386 GNU/Linux
kernel host centos  2.6.18-164.el5 #1 SMP Thu Sep 3 03:33:56 EDT 2009 i686 i686 i386 GNU/Linux

./configure --prefix=/usr/

//php client error
Warning</b>:  socket_read(): unable to read from socket [104]: Connection reset by peer in <b>/opt/httpd/htdocs/client.php</b> on line <b>(line for recv) 

I have tried this with the server on Solaris (no errors) and with the server on Centos as above (errors). Does not seem to matter whether the client is C or php, or on Centos or Linux. 

There is almost always an error if the server is remote, much fewer errors (19 errors in 2696 for response length > MTU ) if the server is on the LAN. The errors can be minimized or reduced to almost nil by adding a sleep (1) delay before closing the socket on the server child thread. 

The key issue seems to be the network delays caused by being remote. 

I have included php and C clients, and the C server code. Search for sleep to see the 'hack' that makes it work much better. 

	//minimizes socket errors if set:
	//sleep(1);
       	close(snew);
	free(outstr);
	return(0);
//file client.c
//simple socket client

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


#define PORTNUMBER 1528

int main(void){

	int n, s, len;
	char buf[1024];
	char hostname[64];
	struct hostent *hp;
	struct sockaddr_in name;
	char msg[1001];
	char slen[11];
	char msgout[20001];
	char msgfull[20001];
	unsigned int nbytes;
	unsigned int ntotalbytes;
	int nflags;

	int nconnect;

	memset(slen, '\0', 11);
	memset(msgout, '\0', 20001);
	memset(msgfull, '\0', 20001);
	memset(msg, '\0', 1001);
	//host
	//sprintf(hostname, "172.22.3.NNN");
	sprintf(hostname, "localhost");
	hp = gethostbyname(hostname);
	s = socket(AF_INET, SOCK_STREAM, 0);

	//Create the address of the server.
	memset(&name, 0, sizeof(struct sockaddr_in));
	name.sin_family = AF_INET;
	name.sin_port = htons(PORTNUMBER);
	memcpy(&name.sin_addr, hp->h_addr_list[0], hp->h_length);
	len = sizeof(struct sockaddr_in);

	nconnect = connect(s, (struct sockaddr *) &name, len);
	printf("gothere nconnect %d\n", nconnect);

	ntotalbytes = nbytes = nflags = 0;

	//static message with length for testing
	sprintf(msg, "000064request message string just to generate a canned response\n");
	n = strlen(msg);

	//send a request
	nbytes = send(s, msg, n, 0); //some request
	if (nbytes < 0) {perror("send");return 1;}

	nflags = 0;
	//get the message length
	nbytes = recv(s, msgout, 6, nflags);
	if (nbytes < 0) {perror("recv");return 1;}

	printf("msg from socket\n%s", msgout);
	n = atoi(msgout);


	//get the message

	nflags = MSG_WAITALL;
	while(1){
		nbytes = recv(s, msgout, n, nflags);
		if (nbytes < 0) {perror("recv");return 1;}
		ntotalbytes += nbytes;
		strcat(msgfull, msgout);
		memset(msgout, '\0', 20001);
		if(0 == nbytes) break;
		if(ntotalbytes >= n) break;
	}//endwhile 1
	printf("msg len %d from socket\n%s", ntotalbytes, msgfull);

	close(s);

	return 0;

}//end main


<?php
//php client
//we can also call the server from php:
   //file client.php 
   //Apache/2.4.2  PHP/5.4.5 

   // Create a TCP/IP Socket
   $socket = socket_create (AF_INET, SOCK_STREAM, 0);
   if ($socket < 0) { echo "socket() failed: reason: " . strerror ($socket) . "\n"; }
   // Connect to Target
   $address = 'localhost';   // Target IP
   $service_port = '1528';   // Target Port

   $result = socket_connect ($socket, $address, $service_port);
   if ($result < 0) { echo "connect() failed.\nReason: ($result) " . strerror($result) . "\n"; }

   $outPost = "000064request message string just to generate a canned response\n";
   // Send Request Transaction
   socket_write ($socket, $outPost, strlen ($outPost));

   // Read Output Transaction
   //get length of response
   $tmp = socket_read ($socket, 6, PHP_NORMAL_READ);
   $slen = (int)($tmp-6);
   $out = (string)$tmp;

   //get response
   while(($sting = socket_read($socket, $slen)) !== false) {

        $out = $out.$sting;

        if(strlen($out) >= $slen) break;

   }
   echo $out."\n";

   // Close Socket
   socket_close ($socket);

?>


//file threadserver.c - server on remote host
//C socket server
#include <stdio.h>
#include <malloc.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <sys/socket.h>
#include <stdlib.h>

#define PORTNUMBER 1528


//globals
pid_t pid;
pid_t pidmain;
int smain, snew;

//please search for sleep below to see how errors can be mitigated

int main(int argc, char **argv, char** envp){
    char wb[200];
    char* pMsg;
    uint msgLen;
    char instr[30001];
    char* outstr;
    int msg_len;
    int numread, bytesread, naddr, len;
    int bytesout, numout;
    struct sockaddr_in name;
    char strOut[6];
    char* pIn;
    char* pOut;
    unsigned short inlen, outlen;
    int oksock;
    struct linger ling;
    int optout;
    int ret;

 //    Create the socket.
    if ((smain = socket(PF_INET, SOCK_STREAM, 0)) < 0) { perror("socket"); exit(EXIT_FAILURE); }

    //Create the address of the server.
    memset(&name, 0, sizeof(struct sockaddr_in));
    name.sin_family = PF_INET;
    name.sin_port = htons(PORTNUMBER);
    len = sizeof(struct sockaddr_in);

 //     Use the wildcard address.
    naddr = INADDR_ANY;
    memcpy(&name.sin_addr, &naddr, sizeof(long));

 //     Bind the socket to the address.
    if (bind(smain, (struct sockaddr *) &name, len) < 0) {perror("bind"); exit(EXIT_FAILURE);}

    // Listen for connections.
    if (listen(smain, 32) < 0) {perror("listen");exit(EXIT_FAILURE);}

    while (1){
        if ((snew = accept(smain, (struct sockaddr *) &name, &len)) < 0) {
            close(snew);
            continue;
        }
        pidmain = fork();

	//parent
        if (pidmain != 0){
                while (wait(&ret) != pidmain) continue;
		//this causes socket errors if remote
		//not closing it causes resource issues
		close(snew);
                continue;
        }//end parent

	//child
        numread = 0;
        memset(instr, '\0', 30001);
	// set socket options
        ling.l_onoff = 1;
        ling.l_linger = 30;
        optout = sizeof(ling);
        oksock = setsockopt( snew, SOL_SOCKET, SO_LINGER, &ling, optout);

        pIn =  instr; //get a pointer we can move around

        numread = read(snew, pIn, 6);

        if ( numread < 6){close(snew);exit(EXIT_FAILURE);}
        bytesread = numread;

        //get msg len
        strncpy(wb, pIn, 6);
        wb[6] = '\0';
        inlen = atoi(wb);
        if ( inlen < 0 || inlen > 30000){ close(snew); exit(EXIT_FAILURE); }
        pIn += 6;
        pMsg = pIn;
        msgLen = inlen  - 6;

        while (1){
            numread = read(snew, pIn, (msgLen));
            pIn += numread;
            if (numread > 0) bytesread += numread;
            if (bytesread >= (int)inlen) break;
        }//endwhile 1

	//build around 5K string
	outstr = (char*)malloc(30001);
        memset(outstr, '\0', 30001);
        memset(outstr, 'A', 5500);

	msg_len = strlen(outstr);

	//get a poiner we can move around
	pOut = outstr;
	//send back the message length
	sprintf(wb, "%6.6d", (msg_len+6+1));
	write(snew, wb, 6);

       	bytesout = numout = 0;
       	while(1){
		numout = write(snew, pOut, (msg_len-bytesout));
		if(-1 == numout) {printf ("Error writing: %s\n", strerror (errno));return 1;}
		pOut += numout;
		if (numout > 0) bytesout += numout;
		if (bytesout >= msg_len) break;
       	}//endwhile 1

	//IMPORTANT IMPORTANT //
	//minimizes socket errors for remote client if set:
	//sleep(1);

       	close(snew);
	free(outstr);
	return(0);
   }//endwhile pre accept
}//end main
Comment 1 herefishy 2012-08-20 22:57:00 UTC
Created attachment 6595 [details]
C client to test the server
Comment 2 herefishy 2012-08-20 22:58:50 UTC
Created attachment 6596 [details]
php client also can be used to test the server
Comment 3 Andreas Schwab 2013-01-15 10:46:27 UTC
glibc is not involved in socket I/O, so if anything is wrong it is either the test program or the kernel.
Comment 4 Florian Weimer 2013-01-15 11:14:21 UTC
(In reply to comment #0)
>     //send a request
>     nbytes = send(s, msg, n, 0); //some request
>     if (nbytes < 0) {perror("send");return 1;}

You should check the value of nbytes.

Closing a streaming socket which still has unread data will abort the connection.  Connection aborts are delivered out of order.  Perhaps this is what you're seeing.  (Stevens' UNIX Network Programming covers this corner case quite well.)