1

I have a small program that first outputs a string to the user and then takes an input. I instead want the program to work by sending and receiving from a port. To try to realize this I ran the command socat TCP4-LISTEN:1337,reuseaddr,fork EXEC:./program. From this command, I wanted to be able to run nc 127.0.0.1 1337 and expected the program to:

  1. Recieve the message from the program
  2. Be able to provide input
  3. Lost connetion

However, when running the program using socat it goes like this

  1. Give input
  2. Message received from the program
  3. Give another input
  4. Lost connection

I don't understand why this happens. Is there any fault on my part using the socat command? And if it is, please tell me what is missing/wrong.

Here is the program.

#include <stdio.h>

void vuln(void) { printf("Input\n"); char buffer[256]; gets(buffer); // potential buffer overflow }

int main(void) { vuln(); return 0; }

Andy Dalton
  • 13,993

1 Answers1

2

In C, printf is part of the buffered I/O library – data written to the stream is "buffered" in memory (i.e., not written directly the the kernel).

By default, stdout (the stream to which printf writes data) is a line-buffered stream, which means that bytes are stored in the in-memory buffer until a newline is written to the stream (or some other even that triggers flushing happens).

If standard output isn't associated with a terminal, then stdout is a block-buffered stream, which means that bytes are stored in the in-memory buffer until the buffer gets full.

When you run your program at the terminal, the '\n' in your call to printf triggers the flush (since it's line-buffered):

$ ./a.out
Input
whatever-you-type
$

If you redirect output to a file, you'll see a different behavior:

Terminal 1               Terminal 2
--------------------     --------------------
$ ./a.out > /tmp/out
                         $ cat /tmp/out
                         $
type-input
$
                         $ cat /tmp/out
                         Input
                         $ 

When you run socat without pts, the output stream is in block-buffered mode; when you run it with pts the output stream is in line-buffered mode.

If you want to override that behavior, you have a couple of options. You can call a function to explicitly cause any buffered data to be flushed:

printf("Input\n");
fflush(stdout);

Alternatively, you can explicitly set the buffering mode of the output stream before calling printf:

setvbuf(stdout, NULL, _IONBF, 0); // _IONBF = Unbuffered
printf("Input\n");

See Chapter 3 of Robert Love's book Linux System Programming for a more detailed explanation.

Andy Dalton
  • 13,993