From 78ae487dcf3f07187844c8435c76ff8b517e6088 Mon Sep 17 00:00:00 2001
From: Chris Tallon <chris@vomp.tv>
Date: Sat, 18 Feb 2006 18:47:06 +0000
Subject: [PATCH] Finished Bootp/TFTP server

---
 bootpd.c                           |  12 +-
 mvpserver.c                        |  53 +++++++-
 tftpclient.c                       | 192 ++++++-----------------------
 tftpclient.h                       |   3 +-
 tftpd.c                            |  15 ++-
 tftpd.h                            |   4 +-
 thread.c                           |   3 +-
 vomp-00-00-00-00-00-00.conf.sample |   5 +
 vomp.conf.sample                   |  12 +-
 9 files changed, 130 insertions(+), 169 deletions(-)

diff --git a/bootpd.c b/bootpd.c
index 1786d5e..0a98fe0 100644
--- a/bootpd.c
+++ b/bootpd.c
@@ -222,7 +222,17 @@ void Bootpd::processRequest(UCHAR* data, int length)
   }
 
   // Set filename
-  strncpy((char*)&data[108], "/mvp/vomp-dongle", 127);
+  char* tftpFileName = config.getValueString("Boot", "TFTP file name");
+  if (tftpFileName)
+  {
+    strncpy((char*)&data[108], tftpFileName, 127);
+    delete[] tftpFileName;
+  }
+  else
+  {
+    data[108] = '-';
+    data[109] = '\0';
+  }
 
   // set to reply
   data[0] = 2;
diff --git a/mvpserver.c b/mvpserver.c
index c1063fa..438405c 100644
--- a/mvpserver.c
+++ b/mvpserver.c
@@ -110,8 +110,22 @@ int MVPServer::run()
     return 0;
   }
 
-  // Start Bootpd
-  if (config.getValueString("General", "Bootp server"))
+  // Read config and start bootp and tftp as appropriate
+
+  char* configString;
+  int bootpEnabled = 0;
+  int tftpEnabled = 0;
+
+  configString = config.getValueString("General", "Bootp server enabled");
+  if (configString && (!strcasecmp(configString, "yes"))) bootpEnabled = 1;
+  if (configString) delete[] configString;
+
+  configString = config.getValueString("General", "TFTP server enabled");
+  if (configString && (!strcasecmp(configString, "yes"))) tftpEnabled = 1;
+  if (configString) delete[] configString;
+
+
+  if (bootpEnabled)
   {
     if (!bootpd.run())
     {
@@ -125,10 +139,38 @@ int MVPServer::run()
     log.log("Main", Log::INFO, "Not starting Bootpd");
   }
 
-  // Start Tftpd
-  if (config.getValueString("General", "TFTP server"))
+  if (tftpEnabled)
   {
-    if (!tftpd.run())
+    char tftpPath[PATH_MAX];
+//    snprintf(configFileName, PATH_MAX, "%s/vomp.conf", configDir);
+
+    configString = config.getValueString("General", "TFTP directory");
+    if (configString)
+    {
+      snprintf(tftpPath, PATH_MAX, "%s", configString);
+
+      // this will never happen.. surely.
+      if ((strlen(tftpPath) + 2) >= PATH_MAX)
+      {
+        delete[] configString;
+        log.log("Main", Log::CRIT, "Could not understand TFTP directory from config");
+        stop();
+        return 0;
+      }
+
+      // if there isn't a / at the end of the dir, add one
+      if (tftpPath[strlen(tftpPath) - 1] != '/') strcat(tftpPath, "/");
+
+      delete[] configString;
+    }
+    else
+    {
+      snprintf(tftpPath, PATH_MAX, "%s/", configDir);
+    }
+
+    log.log("Main", Log::INFO, "TFTP path '%s'", tftpPath);
+
+    if (!tftpd.run(tftpPath))
     {
       log.log("Main", Log::CRIT, "Could not start TFTPd");
       stop();
@@ -140,6 +182,7 @@ int MVPServer::run()
     log.log("Main", Log::INFO, "Not starting TFTPd");
   }
 
+
   // start thread here
   if (!threadStart())
   {
diff --git a/tftpclient.c b/tftpclient.c
index 75d72df..8613339 100644
--- a/tftpclient.c
+++ b/tftpclient.c
@@ -20,9 +20,6 @@
 
 #include "tftpclient.h"
 
-void dump(unsigned char* data, USHORT size);
-unsigned char dcc(UCHAR c);
-
 TftpClient::TftpClient()
 {
   log = Log::getInstance();
@@ -33,6 +30,7 @@ TftpClient::TftpClient()
 
 TftpClient::~TftpClient()
 {
+  log->log("TftpClient", Log::DEBUG, "Someone calls tftpclient::~tftpclient");
   shutdown();
 }
 
@@ -52,11 +50,13 @@ int TftpClient::shutdown()
   return 1;
 }
 
-int TftpClient::run(char* tpeerIP, USHORT tpeerPort, UCHAR* data, int length)
+int TftpClient::run(char* tbaseDir, char* tpeerIP, USHORT tpeerPort, UCHAR* data, int length)
 {
   if (threadIsActive()) return 1;
   log->log("TftpClient", Log::DEBUG, "Client handler started");
 
+  baseDir = tbaseDir;
+
   strncpy(peerIP, tpeerIP, 16);
   peerPort = tpeerPort;
 
@@ -87,7 +87,11 @@ void TftpClient::threadMethod()
 
   // process the first message received by the parent listener
   // the first incoming message is placed in buffer by above run method
-  if (!processMessage(buffer, bufferLength)) return;
+  if (!processMessage(buffer, bufferLength))
+  {
+    log->log("TftpClient", Log::INFO, "threadMethod terminating connection");
+    return;
+  }
 
   int retval;
 
@@ -107,9 +111,9 @@ void TftpClient::threadMethod()
     {
       // 1s timer expired
       // see if we need to retransmit a data packet
-      if (((state == 1) || (state == 2)) && (lastCom < (time(NULL) - 3)))
+      if (((state == 1) || (state == 2)) && (lastCom < (time(NULL) - 1)))
       {
-        log->log("TftpClient", Log::DEBUG, "Retransmitting buffer");
+//        log->log("TftpClient", Log::DEBUG, "Retransmitting buffer");
         transmitBuffer();
       }
 
@@ -134,8 +138,8 @@ void TftpClient::threadMethod()
 
 void TftpClient::threadPostStopCleanup()
 {
-  log->log("TftpClient", Log::DEBUG, "Deleting tftpclient");
-  delete this; // careful
+//  log->log("TftpClient", Log::DEBUG, "Deleting tftpclient");
+//  delete this; // careful
 }
 
 int TftpClient::processMessage(UCHAR* data, int length)
@@ -162,7 +166,8 @@ int TftpClient::processMessage(UCHAR* data, int length)
     }
     case 3: // Data
     {
-      break;
+      log->log("TftpClient", Log::ERR, "Client sent a data packet!");
+      return 0; // quit
     }
     case 4: // Ack
     {
@@ -176,6 +181,7 @@ int TftpClient::processMessage(UCHAR* data, int length)
 
     default:
     {
+      log->log("TftpClient", Log::ERR, "Client TFTP protocol error");
       return 0;
     }
   }
@@ -225,7 +231,7 @@ int TftpClient::processAck(UCHAR* data, int length)
     // successful incoming packet
     lastCom = time(NULL);
 
-    log->log("TftpClient", Log::DEBUG, "Ack received for block %i - success", ackBlock);
+//    log->log("TftpClient", Log::DEBUG, "Ack received for block %i - success", ackBlock);
 
     if (state == 1) // it wasn't the final block
     {
@@ -240,15 +246,34 @@ int TftpClient::processAck(UCHAR* data, int length)
   }
   else
   {
-    log->log("TftpClient", Log::DEBUG, "Ack received for block %i - rejected\n", ackBlock);
+//    log->log("TftpClient", Log::DEBUG, "Ack received for block %i - rejected, retransmitting block\n", ackBlock);
+    transmitBuffer();
   }
 
   return 1;
 }
 
-int TftpClient::openFile(char* filename)
+int TftpClient::openFile(char* requestedFile)
 {
-  file = fopen("/opt/dvb/vomp-dongle-0.1.1", "r");
+  char fileName[PATH_MAX];
+  strcpy(fileName, requestedFile);
+
+  for(UINT i = 0; i < strlen(fileName); i++)
+  {
+    if (fileName[i] == '/')
+    {
+      log->log("TftpClient", Log::ERR, "TFTP filename from client contained a path");
+      return 0;
+    }
+  }
+
+  char fileName2[PATH_MAX];
+  snprintf(fileName2, PATH_MAX, "%s%s", baseDir, fileName);
+
+  log->log("TftpClient", Log::INFO, "File: '%s'", fileName2);
+
+
+  file = fopen(fileName2, "r");
   if (!file) return 0;
   return 1;
 }
@@ -280,142 +305,5 @@ void TftpClient::transmitBuffer()
 {
   ds.send(peerIP, peerPort, (char*)buffer, bufferLength);
 //  dump(buffer, bufferLength);
-  log->log("TftpClient", Log::DEBUG, "Sent block number %i", blockNumber - 1);
+//  log->log("TftpClient", Log::DEBUG, "Sent block number %i", blockNumber - 1);
 }
-
-void dump(unsigned char* data, USHORT size)
-{
-  printf("Size = %u\n", size);
-
-  USHORT c = 0;
-  while(c < size)
-  {
-    if ((size - c) > 15)
-    {
-      printf(" %02X %02X %02X %02X  %02X %02X %02X %02X  %02X %02X %02X %02X  %02X %02X %02X %02X  %c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c\n",
-        data[c], data[c+1], data[c+2], data[c+3], data[c+4], data[c+5], data[c+6], data[c+7],
-        data[c+8], data[c+9], data[c+10], data[c+11], data[c+12], data[c+13], data[c+14], data[c+15],
-        dcc(data[c]), dcc(data[c+1]), dcc(data[c+2]), dcc(data[c+3]), dcc(data[c+4]), dcc(data[c+5]), dcc(data[c+6]), dcc(data[c+7]),
-        dcc(data[c+8]), dcc(data[c+9]), dcc(data[c+10]), dcc(data[c+11]), dcc(data[c+12]), dcc(data[c+13]), dcc(data[c+14]), dcc(data[c+15]));
-      c += 16;
-    }
-    else
-    {
-      switch (size - c)
-      {
-        case 15:
-          printf(" %02X %02X %02X %02X  %02X %02X %02X %02X  %02X %02X %02X %02X  %02X %02X %02X     %c%c%c%c%c%c%c%c%c%c%c%c%c%c%c\n",
-            data[c], data[c+1], data[c+2], data[c+3], data[c+4], data[c+5], data[c+6], data[c+7],
-            data[c+8], data[c+9], data[c+10], data[c+11], data[c+12], data[c+13], data[c+14],
-            dcc(data[c]), dcc(data[c+1]), dcc(data[c+2]), dcc(data[c+3]), dcc(data[c+4]), dcc(data[c+5]), dcc(data[c+6]), dcc(data[c+7]),
-            dcc(data[c+8]), dcc(data[c+9]), dcc(data[c+10]), dcc(data[c+11]), dcc(data[c+12]), dcc(data[c+13]), dcc(data[c+14]));
-          c += 15;
-          break;
-        case 14:
-          printf(" %02X %02X %02X %02X  %02X %02X %02X %02X  %02X %02X %02X %02X  %02X %02X        %c%c%c%c%c%c%c%c%c%c%c%c%c%c\n",
-            data[c], data[c+1], data[c+2], data[c+3], data[c+4], data[c+5], data[c+6], data[c+7],
-            data[c+8], data[c+9], data[c+10], data[c+11], data[c+12], data[c+13],
-            dcc(data[c]), dcc(data[c+1]), dcc(data[c+2]), dcc(data[c+3]), dcc(data[c+4]), dcc(data[c+5]), dcc(data[c+6]), dcc(data[c+7]),
-            dcc(data[c+8]), dcc(data[c+9]), dcc(data[c+10]), dcc(data[c+11]), dcc(data[c+12]), dcc(data[c+13]));
-          c += 14;
-          break;
-        case 13:
-          printf(" %02X %02X %02X %02X  %02X %02X %02X %02X  %02X %02X %02X %02X  %02X           %c%c%c%c%c%c%c%c%c%c%c%c%c\n",
-            data[c], data[c+1], data[c+2], data[c+3], data[c+4], data[c+5], data[c+6], data[c+7],
-            data[c+8], data[c+9], data[c+10], data[c+11], data[c+12],
-            dcc(data[c]), dcc(data[c+1]), dcc(data[c+2]), dcc(data[c+3]), dcc(data[c+4]), dcc(data[c+5]), dcc(data[c+6]), dcc(data[c+7]),
-            dcc(data[c+8]), dcc(data[c+9]), dcc(data[c+10]), dcc(data[c+11]), dcc(data[c+12]));
-          c += 13;
-          break;
-        case 12:
-          printf(" %02X %02X %02X %02X  %02X %02X %02X %02X  %02X %02X %02X %02X               %c%c%c%c%c%c%c%c%c%c%c%c\n",
-            data[c], data[c+1], data[c+2], data[c+3], data[c+4], data[c+5], data[c+6], data[c+7],
-            data[c+8], data[c+9], data[c+10], data[c+11],
-            dcc(data[c]), dcc(data[c+1]), dcc(data[c+2]), dcc(data[c+3]), dcc(data[c+4]), dcc(data[c+5]), dcc(data[c+6]), dcc(data[c+7]),
-            dcc(data[c+8]), dcc(data[c+9]), dcc(data[c+10]), dcc(data[c+11]));
-          c += 12;
-          break;
-        case 11:
-          printf(" %02X %02X %02X %02X  %02X %02X %02X %02X  %02X %02X %02X                  %c%c%c%c%c%c%c%c%c%c%c\n",
-            data[c], data[c+1], data[c+2], data[c+3], data[c+4], data[c+5], data[c+6], data[c+7],
-            data[c+8], data[c+9], data[c+10],
-            dcc(data[c]), dcc(data[c+1]), dcc(data[c+2]), dcc(data[c+3]), dcc(data[c+4]), dcc(data[c+5]), dcc(data[c+6]), dcc(data[c+7]),
-            dcc(data[c+8]), dcc(data[c+9]), dcc(data[c+10]));
-          c += 11;
-          break;
-        case 10:
-          printf(" %02X %02X %02X %02X  %02X %02X %02X %02X  %02X %02X                     %c%c%c%c%c%c%c%c%c%c\n",
-            data[c], data[c+1], data[c+2], data[c+3], data[c+4], data[c+5], data[c+6], data[c+7],
-            data[c+8], data[c+9],
-            dcc(data[c]), dcc(data[c+1]), dcc(data[c+2]), dcc(data[c+3]), dcc(data[c+4]), dcc(data[c+5]), dcc(data[c+6]), dcc(data[c+7]),
-            dcc(data[c+8]), dcc(data[c+9]));
-          c += 10;
-          break;
-        case 9:
-          printf(" %02X %02X %02X %02X  %02X %02X %02X %02X  %02X                        %c%c%c%c%c%c%c%c%c\n",
-            data[c], data[c+1], data[c+2], data[c+3], data[c+4], data[c+5], data[c+6], data[c+7],
-            data[c+8],
-            dcc(data[c]), dcc(data[c+1]), dcc(data[c+2]), dcc(data[c+3]), dcc(data[c+4]), dcc(data[c+5]), dcc(data[c+6]), dcc(data[c+7]),
-            dcc(data[c+8]));
-          c += 9;
-          break;
-        case 8:
-          printf(" %02X %02X %02X %02X  %02X %02X %02X %02X                            %c%c%c%c%c%c%c%c\n",
-            data[c], data[c+1], data[c+2], data[c+3], data[c+4], data[c+5], data[c+6], data[c+7],
-            dcc(data[c]), dcc(data[c+1]), dcc(data[c+2]), dcc(data[c+3]), dcc(data[c+4]), dcc(data[c+5]), dcc(data[c+6]), dcc(data[c+7]));
-          c += 8;
-          break;
-        case 7:
-          printf(" %02X %02X %02X %02X  %02X %02X %02X                               %c%c%c%c%c%c%c\n",
-            data[c], data[c+1], data[c+2], data[c+3], data[c+4], data[c+5], data[c+6],
-            dcc(data[c]), dcc(data[c+1]), dcc(data[c+2]), dcc(data[c+3]), dcc(data[c+4]), dcc(data[c+5]), dcc(data[c+6]));
-          c += 7;
-          break;
-        case 6:
-          printf(" %02X %02X %02X %02X  %02X %02X                                  %c%c%c%c%c%c\n",
-            data[c], data[c+1], data[c+2], data[c+3], data[c+4], data[c+5],
-            dcc(data[c]), dcc(data[c+1]), dcc(data[c+2]), dcc(data[c+3]), dcc(data[c+4]), dcc(data[c+5]));
-          c += 6;
-          break;
-        case 5:
-          printf(" %02X %02X %02X %02X  %02X                                     %c%c%c%c%c\n",
-            data[c], data[c+1], data[c+2], data[c+3], data[c+4],
-            dcc(data[c]), dcc(data[c+1]), dcc(data[c+2]), dcc(data[c+3]), dcc(data[c+4]));
-          c += 5;
-          break;
-        case 4:
-          printf(" %02X %02X %02X %02X                                         %c%c%c%c\n",
-            data[c], data[c+1], data[c+2], data[c+3],
-            dcc(data[c]), dcc(data[c+1]), dcc(data[c+2]), dcc(data[c+3]));
-          c += 4;
-          break;
-        case 3:
-          printf(" %02X %02X %02X                                            %c%c%c\n",
-            data[c], data[c+1], data[c+2],
-            dcc(data[c]), dcc(data[c+1]), dcc(data[c+2]));
-          c += 3;
-          break;
-        case 2:
-          printf(" %02X %02X                                               %c%c\n",
-            data[c], data[c+1],
-            dcc(data[c]), dcc(data[c+1]));
-          c += 2;
-          break;
-        case 1:
-          printf(" %02X                                                  %c\n",
-            data[c],
-            dcc(data[c]));
-          c += 1;
-          break;
-      }
-    }
-  }
-}
-
-unsigned char dcc(UCHAR c)
-{
-  if (isspace(c)) return ' ';
-  if (isprint(c)) return c;
-  return '.';
-}
-
diff --git a/tftpclient.h b/tftpclient.h
index 2a1bdc6..5a80c2c 100644
--- a/tftpclient.h
+++ b/tftpclient.h
@@ -37,12 +37,13 @@ class TftpClient : public Thread
     TftpClient();
     virtual ~TftpClient();
 
-    int run(char* ip, USHORT port, UCHAR* data, int length);
+    int run(char* baseDir, char* ip, USHORT port, UCHAR* data, int length);
     int shutdown();
 
   private:
     Log* log;
     DatagramSocket ds;
+    char* baseDir;
     char peerIP[17];
     USHORT peerPort;
     UCHAR buffer[600];
diff --git a/tftpd.c b/tftpd.c
index 564b2c5..38415e0 100644
--- a/tftpd.c
+++ b/tftpd.c
@@ -23,6 +23,7 @@
 Tftpd::Tftpd()
 {
   log = Log::getInstance();
+  baseDir = NULL;
 }
 
 Tftpd::~Tftpd()
@@ -35,13 +36,16 @@ int Tftpd::shutdown()
   if (threadIsActive()) threadCancel();
   ds.shutdown();
 
+  if (baseDir) delete[] baseDir;
+  baseDir = NULL;
+
   return 1;
 }
 
-int Tftpd::run()
+int Tftpd::run(char* tbaseDir)
 {
   if (threadIsActive()) return 1;
-  log->log("Tftpd", Log::DEBUG, "Starting Tftpd");
+  log->log("Tftpd", Log::DEBUG, "Starting TFTPd");
 
   if (!ds.init(16869))
   {
@@ -50,6 +54,9 @@ int Tftpd::run()
     return 0;
   }
 
+  baseDir = new char[strlen(tbaseDir) + 1];
+  strcpy(baseDir, tbaseDir);
+
   if (!threadStart())
   {
     log->log("Tftpd", Log::DEBUG, "Thread start error");
@@ -57,7 +64,7 @@ int Tftpd::run()
     return 0;
   }
 
-  log->log("Tftpd", Log::DEBUG, "Bootp replier started");
+  log->log("Tftpd", Log::DEBUG, "TFTP server started with base path '%s'", baseDir);
   return 1;
 }
 
@@ -82,7 +89,7 @@ void Tftpd::threadMethod()
     else
     {
       TftpClient* t = new TftpClient();
-      t->run(ds.getFromIPA(), ds.getFromPort(), (UCHAR*)ds.getData(), ds.getDataLength());
+      t->run(baseDir, ds.getFromIPA(), ds.getFromPort(), (UCHAR*)ds.getData(), ds.getDataLength());
     }
   }
 }
diff --git a/tftpd.h b/tftpd.h
index f866feb..50f2ede 100644
--- a/tftpd.h
+++ b/tftpd.h
@@ -38,7 +38,7 @@ class Tftpd : public Thread
     Tftpd();
     virtual ~Tftpd();
 
-    int run();
+    int run(char* baseDir);
     int shutdown();
 
   private:
@@ -46,6 +46,8 @@ class Tftpd : public Thread
 
     DatagramSocket ds;
     Log* log;
+
+    char* baseDir;
 };
 
 #endif
diff --git a/thread.c b/thread.c
index 0eadacb..851c024 100755
--- a/thread.c
+++ b/thread.c
@@ -59,7 +59,6 @@ void Thread::threadStop()
   // Signal thread here in case it's waiting
   threadSignal();
   pthread_join(pthread, NULL);
-  this->threadPostStopCleanup();
 }
 
 void Thread::threadCancel()
@@ -67,7 +66,7 @@ void Thread::threadCancel()
   threadActive = 0;
   pthread_cancel(pthread);
   pthread_join(pthread, NULL);
-  this->threadPostStopCleanup();
+  this->threadPostStopCleanup(); // thread was cancelled, did not run post-stop above
 }
 
 void Thread::threadCheckExit()
diff --git a/vomp-00-00-00-00-00-00.conf.sample b/vomp-00-00-00-00-00-00.conf.sample
index 35d2fea..061c197 100644
--- a/vomp-00-00-00-00-00-00.conf.sample
+++ b/vomp-00-00-00-00-00-00.conf.sample
@@ -10,6 +10,11 @@
 
 # Override IP = 1
 
+## A file name to send to the MVP. The MVP will then request
+## this file from the TFTP server. Don't use a path here!
+
+# TFTP file name = vomp-dongle
+
 [General]
 
 ## The client auto-detects whether to use PAL or NTSC. If you
diff --git a/vomp.conf.sample b/vomp.conf.sample
index c52e473..722c1af 100644
--- a/vomp.conf.sample
+++ b/vomp.conf.sample
@@ -8,17 +8,23 @@
 ## can enter a name here that will appear on the
 ## server select list on the MVP
 
-# Server name = MyServer
+# Server name = My-Server
 
 ## Enable this to start the built in Bootp server
 ## Required to boot the MVP if you have not got a
 ## DHCP server that can tell the MVP its boot file
 ## name and server
 
-# Bootp server = 1
+# Bootp server enabled = yes
 
 ## Enable this to start the built in TFTP server
 ## Required to boot the MVP if you have not got a
 ## TFTP server running elsewhere
 
-# TFTP server = 1
+# TFTP server enabled = yes
+
+## Base directory for TFTP server
+## If you leave this blank the plugin config
+## path will be used - i.e. where this file is
+
+# TFTP directory = /tftpboot
-- 
2.39.5