UNIX Socket FAQ

A forum for questions and answers about network programming on Linux and all other Unix-like systems

You are not logged in.

  • Index
  • » C
  • » TFTP - netascii

#1 2009-08-25 07:05 PM

Tommo
Member
Registered: 2007-09-03
Posts: 76

Re: TFTP - netascii

Hi.

I'm trying to write a tftp server and client. So far, so good. The server and client can send binary files back and forth.  However, I am a little confused about netascii mode.  Firstly, why does the server need to know whether it is sending ascii or binary?  When the client submits a read/write request to the server, the request packet contains the mode (i.e. binary or netascii) so the server must need to know for some reason.  I thought the server could just send the packet and the client could then deal with it (i.e. stick an '\0' on at the end and replace "\r\n" with "\n" (for windows servers))?

Offline

#2 2009-08-25 08:40 PM

RobSeace
Administrator
From: Boston, MA
Registered: 2002-06-12
Posts: 3,839
Website

Re: TFTP - netascii

In binary mode, both sides (client and server) agree not to tamper with the data, and
just send/receive it as-is...  In ascii mode, it's assumed you're sending a text file, and
since different systems have different notions about how to properly store text files
(end of line characters being the primary difference), the sender converts from its
local method to the standard NVT ASCII, a la telnet, and the receiver can then know
what format to expect the data in, so it can convert it back to its own local format...
Of course, if both sender and receiver are on the same type of system, it's just
wasted effort, and sending the file in binary will work fine...  But, eg. transmitting a
text file from Windoze to Unix in binary mode will yield a file loaded with pointless
carriage-return characters (and possibly a CTRL-Z at the end)...  But, transmitting it
in ascii mode will result in the CRs getting stripped on the Unix end, before saving...
(In that case, the Windoze sender doesn't need to do much to convert the file to NVT
ASCII for transmission, since CRLF is already specified as the NVT EOL sequence...
But, it may need to do some other stuff, like stripping CTRL-Z from the end...  But, in
the opposite case with a Unix sender, it will have to add CRs in front of every LF
before sending...)

Offline

#3 2009-08-25 10:09 PM

Tommo
Member
Registered: 2007-09-03
Posts: 76

Re: TFTP - netascii

Thanks Rob.  I looked up NVT-ASCII in 'TCP/IP Illustrated' and apparently all that is needed is to change a new line to 'CR LF' and a single carriage return to 'CR NUL'.  I couldn't find anything about the standard NVT ASCII End-of-File codes like you were talking about. I realise for Windoze it's ^Z (26), and for Linux/Unix it is ^D (04).

Offline

#4 2009-08-26 01:21 PM

RobSeace
Administrator
From: Boston, MA
Registered: 2002-06-12
Posts: 3,839
Website

Re: TFTP - netascii

Offline

#5 2009-08-26 01:47 PM

Tommo
Member
Registered: 2007-09-03
Posts: 76

Re: TFTP - netascii

$ cat > file.txt
hello, world
^D
$ cat file.txt
hello, world

Offline

#6 2009-08-28 10:07 PM

Tommo
Member
Registered: 2007-09-03
Posts: 76

Re: TFTP - netascii

Having a slight problem sending ascii.

Here, we convert to nvt ascii and store in buf (which points to a static buffer of size 512 bytes).

#define PACKET_LEN  512

int send_data (char *mode, FILE *fp, char *buf)
{
  int nbytes = 0, c;
  static int x = 0;
  
  if (strcmp ("netascii", mode) == 0)
  {    
    /* sometimes buffer isn't big enough to
       stick "\r\n" on at end and so the best
       we can do is stick \r on and then \n 
       goes at beginning of next buffer
    */
    if (x) {buf[nbytes++]='\n';x=0;}
    
    while ((c = fgetc (fp)) != EOF && nbytes < PACKET_LEN)
    {
      if (c == '\n')
      {
        buf[nbytes++] = '\r';
        
        if (nbytes == PACKET_LEN){x=1;break;}     
        else buf[nbytes++] = '\n';
        
      }
      else buf[nbytes++] = c;
    }
    
  }
  else
  {
    nbytes = fread (buf, 1, PACKET_LEN, fp);
  }
  
  return nbytes;
}

Note that tftp packets are sent in 512 byte chunks.  If the sender sends less then it's the end of transmission.  Thus, the only time this function should return a number less than 512 is when sending the last packet.  A problem I quickly spotted was what happens if we try to replace '\n' with '\r\n' but there is no room left for two bytes in buf, only one.  That is why I have the 'static int x' there - to keep track of this.

Now, this is what is going on at the receiver's end:

void save_to_file (char *mode, int bytes_read, FILE *fp, char *buf)
{
  int nbytes = 0;
  
  printf ("bytes read = %d\n", bytes_read);

  /* if ascii mode */
  if (strcmp (mode, "netascii") == 0)
  {
    while (nbytes < bytes_read)
    {
      if (buf[nbytes] == '\r') 
        nbytes++;

        /* corrected, thanks Rob */

      else
      {
        fputc ((int)buf[nbytes], fp);
        nbytes++;
      }
      
    }
  }
  /* else binary mode */
  else
  {
    fwrite (buf, 1, bytes_read, fp);
  }
}

Here we take buf and convert from nvt ascii to unix ascii (i.e. \r\n -> \n), then store in file corresponding to fp.  Again, 'bytes_read' will only be less than 512 at end of transmission.

Now, I have been staring at this for a while now and I cannot seem to spot what I have done wrong.  Say the sender sends n bytes, and it takes m packets to transfer the entire file (so many lots of 512 + the last packet), then the receiver's file is always (n - (m-1)) short of the sender's file (transferring between the same system).  So clearly each time one of these functions is invoked, it is skipping a byte in the file/buffer and thus not sending/saving it properly.

Can anyone spot the mistake? It's bugging me to hell.

Offline

#7 2009-08-29 05:49 PM

RobSeace
Administrator
From: Boston, MA
Registered: 2002-06-12
Posts: 3,839
Website

Re: TFTP - netascii

No mistake, but you really don't need that special handling for '\n' in the receiver...
Just let it fall into the normal char case, since it's handled exactly the same...  All you
need to do is basically ignore '\r', which you're doing...  (That isn't totally proper
behavior, since you probably should handle '\r\0' for a plain non-EOL CR...  But,
in practice, you probably don't really care about such things, so just ignoring all CRs
is probably fine...)

I'm not sure what the problem is that you're seeing?  The received file is incorrect?
In what way does it differ from the source file?  Are you sure the source file isn't a
DOS file or something, and already contain CRs, so your adding them in the sender
ends up being redundant?

You say the amount of data sent differs from the size of the file...  Well, of course,
that's to be expected, because you're skipping over all CRs!  The data on the wire
will be NVT ASCII with CRLF EOLs, and you're saving it in Unix format with just LF
EOLs...  So, of course the file size will be a bit less, depending on how many lines
are in the file...  So, if that's all you're seeing, then there's no problem at all...  But,
if the file you save is actually corrupted somehow, then explain in what way the
saved data differs from the original source file...

Offline

#8 2009-08-29 08:33 PM

Tommo
Member
Registered: 2007-09-03
Posts: 76

Re: TFTP - netascii

Thanks for the reply Rob.

The problem is that the file the receiver gets is smaller than the file the sender sends.  I'm running Linux here, and just testing out things locally:

I run the server...

$ ./tftp_server

Now I connect using the client...

$ ./tftp_client 127.0.0.1
ftp> get server.txt
bytes read = 512
bytes read = 512
.......  (70 lots of 512)
bytes read = 512
bytes read = 512
bytes read = 102
ftp> quit
$ ls -l server.txt
-rw-r--r-- 1 thomas thomas 35381 2009-08-29 20:11 server.txt

When the client connected, the server began sending...

Sent 512 bytes to client
Sent 512 bytes to client
.... (70 lots of 512)
Sent 512 bytes to client
Sent 512 bytes to client
Sent 102 bytes to client
Server ready for another request...

^C
$ ls -l server.txt
-rw-r--r-- 1 thomas thomas 35449 2009-08-29 00:26 server.txt

So that's a difference of 68 bytes (35449 - 35381).  I quickly inspected the file on the server's side using 'od -c server.txt' but I couldn't see any CR's.  I'm a bit confused.  Here is a brief output of 'diff':

~/tftp/server $ diff ./server.txt ../client/server.txt
7c7
< [    0.000000]  BIOS-e820: 0000000000100000 - 000000003f688c00 (usable)
---
> [    0.000000]  BIOS-e820: 000000000100000 - 000000003f688c00 (usable)
14c14
< [    0.000000]  BIOS-e820: 00000000fee00000 - 00000000fef00000 (reserved)
---
> [    0.000000]  BIOS-e820 00000000fee00000 - 00000000fef00000 (reserved)
23c23
< [    0.000000]   HighMem    229376 ->   259720
---
> [    0.000000]   HighMem    229376 ->   25720

So in the first case there is a '0' missing, in the second there's a ':' missing, etc.  So that's why I thought the perhaps the receiver is skipping a character on (almost) each iteration.

Btw, server.txt was just created using 'dmesg > server.txt'.

Edit: I just wrote a program to see if there were any rogue CR's - it found none.

Offline

#9 2009-08-30 04:55 PM

RobSeace
Administrator
From: Boston, MA
Registered: 2002-06-12
Posts: 3,839
Website

Re: TFTP - netascii

Can you post full code for both server and client (or links to them)?  If there's a bug,
I think it must be outside those two functions...  Unless I'm just missing it as well...

Offline

#10 2009-08-30 04:57 PM

RobSeace
Administrator
From: Boston, MA
Registered: 2002-06-12
Posts: 3,839
Website

Re: TFTP - netascii

No, wait a minute, I see it now!

while ((c = fgetc (fp)) != EOF && nbytes < PACKET_LEN)

Reverse those two tests!  As it is, you're always reading a char from the file BEFORE
you test if you've got room in the buffer for it...  And, if you don't have room, you'll
throw that char away...  That's the problem, I'm sure...

Offline

#11 2009-08-30 05:52 PM

Tommo
Member
Registered: 2007-09-03
Posts: 76

Re: TFTP - netascii

Haha well spotted Rob! That's been bugging me for a while.

Offline

  • Index
  • » C
  • » TFTP - netascii

Board footer

Powered by FluxBB