1

Today I have encountered some weird looking code that at first glance it's not apparent to me what it does.

  send(file_desc,"Input \'y\' to continue.\t",0x18,0);
  read(file_desc,buffer,100);
  iVar1 = strcmp("y",(char *)buffer);
  if (iVar1 == 0) {
    // some more code
  }

It seems that a text string is being written into the file descriptor. Immediately then after that it reads from that file descriptor into a buffer. And it compares if the text written into the buffer is a "y".

My understanding (please correct me if I am wrong), is that it writes some data which is a text string into the file descriptor, and then the file descriptor acts as a temporary storage location for anything you write to it. And after that it reads that data from the file descriptor into the buffer. It actually is the same file descriptor. It seems as a primitive way of using a file descriptor to copy data from the text string into the buffer. Why not just use a strcpy() instead?

What would be the use case of writing to a file descriptor and then immediately read from it? It seems like a convoluted way to copy data using file descriptors. Or maybe I don't understand this code well enough, what this sequence of a send() and a read() does?

And assuming that this code is instead using the file descriptor to copy the text string "Input \'y\' to continue.\t" into the buffer, why are they comparing it with the string "y"? It should probably be false every single time.

I am assuming that any data written into a file descriptor stays in that file descriptor until it is read from. So here it seems that send() is being used to write the string into, and read() is used to read it back out.

In man send it says:

 The only difference between send() and write(2) is the presence of flags.  With a zero
       flags argument, send() is equivalent to write(2).

why would they use send() instead of write()? This code is just so mind boggling.


Edit: here's the full function where this code is originally from:

void send_read(int file_desc)

{
  int are_equal;
  undefined2 buffer [8];
  char local_28 [32];

                    /* 0x6e == 110 == 'n' */
  buffer[0] = 0x6e;
  send(file_desc,"Input \'y\' to continue.\t",0x18,0);
  read(file_desc,buffer,100);
  are_equal = strcmp("y",(char *)buffer);
  if (are_equal == 0) {
    FUN_00400a86(file_desc,local_28);
  }
  else {
    close(file_desc);
  }
  return;
}
Galaxy
  • 2,363
  • 2
  • 25
  • 59
  • 1
    You need to include more of the code.A "file descriptor" does not always mean an actual file on disk. It is more likely a socket, pipe or some other pseudo file. The code would not make sense if it were actually a file on disk. – kaylum Apr 18 '20 at 05:24
  • @JonathanLeffler That clears up a bit for me. So does it uses the same file descriptor for both writing a message to another server, and it also uses it for retrieving the server's response? That would explain the text string that is sent and the following `strcmp()`. I should mention that this code indeed communicates with a server *somewhere*. – Galaxy Apr 18 '20 at 05:43

2 Answers2

0

The send() and recv() functions are for use with sockets (send: send a message on a socket — recv: receive a message from a connected socket). See also the POSIX description of Sockets in general.

Socket file descriptors are bi-directional — you can read and write on them. You can't read what you wrote, unlike with pipe file descriptors. With pipes, the process writing to the write end of a pipe can read what it wrote from the read end of the pipe — if another process didn't read it first. When a process writes on a socket, that information goes to the peer process and cannot be read by the writer.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • Do you think that this code `send()`s the text string *to the server*, and then `read()`s the server's responde? – Galaxy Apr 18 '20 at 05:44
  • I was under the impression that you typically needed to have two separate sockets when communicating with servers: one to send information, and another separate socket to read data that the server sent you. – Galaxy Apr 18 '20 at 05:45
  • 1
    That's basically the idea — it isn't certain that this is the client, but it probably is. It sends to its peer, and receives the response back. – Jonathan Leffler Apr 18 '20 at 05:46
  • 2
    @Galaxy: I'm not sure where you got the impression that you needed two sockets to talk between client and server. That's true with anonymous pipes, but not with sockets — whether Unix-domain or network-connected sockets. – Jonathan Leffler Apr 18 '20 at 05:47
  • Yeah, I was probably thinking about pipes, because other than regular files, pipes were the among the first kind of file descriptors that I learned. – Galaxy Apr 18 '20 at 05:49
  • @JonathanLeffler, a pipe (or a fifo) is not a socket. I'm not sure if you can use `send` or `recve` with fifos. – Luis Colorado Apr 18 '20 at 17:22
  • I'm not sure, @LuisColorado, what gave you the impression that I'm trying to say that a pipe (or FIFO) is a socket. You can't use `recv()` or `send()` with file descriptors for pipes or sockets. I've clarified (I trust) what I mean about reading and writing from pipe descriptors — but that is `read()` and `write()` and variants thereupon, not `recv()` or `send()`. – Jonathan Leffler Apr 18 '20 at 17:28
  • @JonathanLeffler, fine, IMHO the clarification you do now is far better, as your comment before allow to interpret what I said. – Luis Colorado Apr 18 '20 at 17:38
  • `send(X, Y, Z, 0)` is exactly equivalent to `write(X, Y, Z)`, and `recv(X, Y, Z, 0)` is exactly equivalent to `read(X, Y, Z)`. On Linux, they're calling the exact same code in the kernel. – Brian Silverman Nov 21 '21 at 07:23
0

send(2) is a system call that can only be used with sockets. A socket is a descriptor that allows you to use it to send data or receive from a remote point (a remote socket) that can be on a different computer or in the same as you are. But it works like a phone line, what you send is received by your parnter and what he/she sends is received by you. read(2) system call can be used by sockets, while send(2) cannot be used by files, so your sample code is mixing calls related to files with calls related to sockets (that's not uncommon, as read(2) and write(2) can both be used with sockets)

The code you post above is erroneous, as it blindly compares the received buffer with strcmp function, assuming that it received a null terminated string. This can be the case, but it also cannot.

Even if the sender (in the other side of the connection) agreed on sending a full message, nul terminated string. The receiver must first get the amount of data received (this is the return value of the read(2) call, which can be:

  • -! indicating some error on reception. The connection can be reset by the other side, or the other side can have rebooted while you send the data.
  • 0 indicating no more data or end of data (the other side closed the connection) This can happen if the other side has a timeout and you take too much to respond. It closes the connection without sending anything. You just receive nothing.
  • n some data, less than the buffer size, but including the full packet sent by the peer (and the agreed nul byte it sent with it). This is the only case in which you can safely strcmp the data.
  • n some data, less than the buffer size, and less than the data transmitted. This can happen due to some data fragmentation of the data in several packets. Then you have to do another read until you have all the data send by your peer. Packet fragmentation is something natural in TCP, for example.
  • n some data, less than the buffer size, and more than the data transmitted. The sender did another transmit, after the one you receive, and both packets got into the kernel buffer. You have to investigate this case, as you have one full packet, and must save the rest of the received data in the buffer, for later processing, or you'll lose data you have received.
  • n some data, the full buffer filled, and no space to store the full transmitted data remained. You have filled the buffer and no \0 char came... the packet is larger than the buffer, you run out of buffer space and have to decide what to do (allocate other buffer to receive the rest, discard the data, or whatever you decide to do) This will not happen to you because you expect a packet of 1 or 2 characters, and you have a buffer of 100, but who knows...

At least, and as a minimum safe net, you can do this:

  send(file_desc,"Input \'y\' to continue.\t",0x18,0);
  int n = read(file_desc,buffer,sizeof buffer - 1);  /* one cell reserved for '\0' */
  switch (n) {
  case -1: /* error */
      do_error();
      break;
  case 0: /* disconnect */
      do_disconnect();
      break;
  default: /* some data */
      buffer[n] = '\0';  /* append the null */
      break;
  }
  if (n > 0) {
      iVar1 = strcmp("y",(char *)buffer);
      if (iVar1 == 0) {
        // some more code
      }
  }

Note:

As you didn't post a complete and verifiable example, I couldn't post a complete and verifiable response.

My apologies for that.

Community
  • 1
  • 1
Luis Colorado
  • 10,974
  • 1
  • 16
  • 31
  • Good point. Thank you dear sir. I should mention that this code is some kind of an internal protocol implementation, meaning that the same person wrote both the client and the server of the application, so they agreed on exactly the format of the sent and revieved data. – Galaxy Apr 18 '20 at 17:30
  • Hahahaha... just wait to upvote until I finish completing my answer :) Thanks for the comment. – Luis Colorado Apr 18 '20 at 17:35
  • `send(X, Y, Z, 0)` is exactly equivalent to `write(X, Y, Z)`, and `recv(X, Y, Z, 0)` is exactly equivalent to `read(X, Y, Z)`. On Linux, they're calling the exact same code in the kernel. – Brian Silverman Nov 21 '21 at 07:24
  • @BrianSilverman, if you execute the `send()` call to a non-socket you will get an error, while the `write()` syscall will execute properly. So both calls are not exactly equal, despite than when properly passed parameters both execute the same code. I've just tested with a simple `write(1, "Hello, world\n", 13);` and `send(1, "Hello, world\n", 13, 0);`. – Luis Colorado Nov 21 '21 at 14:04
  • @BrianSilverman, if it was the case that the program is executed with a socket in stdout, then both calls would have executed fine, and sent the message over the socket, by executing the same kernel code, but this doesn't mean they are exactly equivalent. – Luis Colorado Nov 21 '21 at 14:07