Client - Fix #7 - Buffer oveflow when data cached across ReadLien calls
[tpg/opendispense2.git] / src / client / protocol.c
index 483ee57..079a4ff 100644 (file)
@@ -9,9 +9,11 @@
  * This file is licenced under the 3-clause BSD Licence. See the file
  * COPYING for full details.
  */
+//#define DEBUG_TRACE_SERVER   2
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <assert.h>
 #include <netdb.h>     // gethostbyname
 #include <sys/socket.h>
 #include <netinet/in.h>
@@ -21,6 +23,7 @@
 #include <unistd.h>    // close/getuid
 #include <limits.h>    // INT_MIN/INT_MAX
 #include <stdarg.h>
+#include <ctype.h>     // isdigit
 #include "common.h"
 
 // === PROTOTYPES ===
@@ -55,8 +58,6 @@ int OpenConnection(const char *Host, int Port)
                return -1;
        }
 
-//     printf("geteuid() = %i, getuid() = %i\n", geteuid(), getuid());
-       
        if( geteuid() == 0 || getuid() == 0 )
        {
                 int    i;
@@ -74,8 +75,6 @@ int OpenConnection(const char *Host, int Port)
                }
                if( i == 1024 )
                        printf("Warning: AUTOAUTH unavaliable\n");
-//             else
-//                     printf("Bound to 0.0.0.0:%i\n", i);
        }
        
        if( connect(sock, (struct sockaddr *) &serverAddr, sizeof(serverAddr)) < 0 ) {
@@ -514,6 +513,105 @@ int Dispense_ItemInfo(int Socket, const char *Type, int ID)
        return 0;
 }
 
+int DispenseCheckPin(int Socket, const char *Username, const char *Pin)
+{
+        int    ret, responseCode;
+       char    *buf;
+       
+       if( strlen(Pin) != 4 ) {
+               fprintf(stderr, "Pin format incorrect (not 4 characters long)\n");
+               return RV_ARGUMENTS;
+       }
+               
+       for( int i = 0; i < 4; i ++ ) {
+               if( !isdigit(Pin[i]) ) {
+                       fprintf(stderr, "Pin format incorrect (character %i not a digit)\n", i);
+                       return RV_ARGUMENTS;
+               }
+       }
+       
+       sendf(Socket, "PIN_CHECK %s %s\n", Username, Pin);
+       buf = ReadLine(Socket);
+       
+       responseCode = atoi(buf);
+       switch( responseCode )
+       {
+       case 200:       // Pin correct
+               printf("Pin OK\n");
+               ret = 0;
+               break;
+       case 201:
+               printf("Pin incorrect\n");
+               ret = RV_INVALID_USER;
+               break;
+       case 401:
+               printf("Not authenticated\n");
+               ret = RV_PERMISSIONS;
+               break;
+       case 403:
+               printf("Only coke members can check accounts other than their own\n");
+               ret = RV_PERMISSIONS;
+               break;
+       case 404:
+               printf("User '%s' not found\n", Username);
+               ret = RV_INVALID_USER;
+               break;
+       case 407:
+               printf("Rate limited or client-server disagree on pin format\n");
+               ret = RV_SERVER_ERROR;
+               break;
+       default:
+               printf("Unknown response code %i ('%s')\n", responseCode, buf);
+               ret = RV_UNKNOWN_ERROR;
+               break;
+       }
+       free(buf);
+       return ret;
+}
+
+int DispenseSetPin(int Socket, const char *Pin)
+{
+        int    ret, responseCode;
+       char    *buf;
+       
+       if( strlen(Pin) != 4 ) {
+               fprintf(stderr, "Pin format incorrect (not 4 characters long)\n");
+               return RV_ARGUMENTS;
+       }
+               
+       for( int i = 0; i < 4; i ++ ) {
+               if( !isdigit(Pin[i]) ) {
+                       fprintf(stderr, "Pin format incorrect (character %i not a digit)\n", i);
+                       return RV_ARGUMENTS;
+               }
+       }
+       
+       sendf(Socket, "PIN_SET %s\n", Pin);
+       buf = ReadLine(Socket);
+       
+       responseCode = atoi(buf);
+       switch(responseCode)
+       {
+       case 200:
+               printf("Pin Updated\n");
+               ret = 0;
+               break;
+       case 401:
+               printf("Not authenticated\n");
+               ret = RV_PERMISSIONS;
+               break;
+       case 407:
+               printf("Client/server disagreement on pin format\n");
+               ret = RV_SERVER_ERROR;
+               break;
+       default:
+               printf("Unknown response code %i ('%s')\n", responseCode, buf);
+               ret = RV_UNKNOWN_ERROR;
+               break;
+       }
+       return ret;
+}
+
 /**
  * \brief Dispense an item
  * \return Boolean Failure
@@ -605,7 +703,7 @@ int Dispense_AlterBalance(int Socket, const char *Username, int Ammount, const c
                rv = RV_BAD_ITEM;
                break;
        case 403:       // Not in coke
-               fprintf(stderr, "You are not in coke (sucker)\n");
+               fprintf(stderr, "Permissions error: %s\n", buf+4);
                rv = RV_PERMISSIONS;
                break;
        case 404:       // Unknown user
@@ -646,7 +744,7 @@ int Dispense_SetBalance(int Socket, const char *Username, int Balance, const cha
        switch(responseCode)
        {
        case 200:       return 0;       // OK
-       case 403:       // Not in coke
+       case 403:       // Not an administrator
                fprintf(stderr, "You are not an admin\n");
                return RV_PERMISSIONS;
        case 404:       // Unknown user
@@ -1080,15 +1178,15 @@ int Dispense_SetItem(int Socket, const char *Type, int ID, int NewPrice, const c
 // ===
 // Helpers
 // ===
+/// Read from the input socket until a newline is seen
 char *ReadLine(int Socket)
 {
        static char     buf[BUFSIZ];
-       static int      bufPos = 0;
        static int      bufValid = 0;
-        int    len;
+        int    len = 0;
        char    *newline = NULL;
         int    retLen = 0;
-       char    *ret = malloc(10);
+       char    *ret = malloc(32);
        
        #if DEBUG_TRACE_SERVER
        printf("ReadLine: ");
@@ -1097,41 +1195,55 @@ char *ReadLine(int Socket)
        
        ret[0] = '\0';
        
+       // While a newline hasn't been seen
        while( !newline )
        {
+               assert(bufValid < BUFSIZ);
+               // If there is data left over from a previous call, use the data from that for the first pass
                if( bufValid ) {
                        len = bufValid;
+                       bufValid = 0;
                }
                else {
-                       len = recv(Socket, buf+bufPos, BUFSIZ-1-bufPos, 0);
+                       // Otherwise read some data
+                       len = recv(Socket, buf, BUFSIZ, 0);
                        if( len <= 0 ) {
                                free(ret);
                                return strdup("599 Client Connection Error\n");
                        }
                }
-               buf[bufPos+len] = '\0';
+               assert(len < BUFSIZ);
+               buf[len] = '\0';
                
-               newline = strchr( buf+bufPos, '\n' );
+               // Search for newline in buffer
+               newline = strchr( buf, '\n' );
                if( newline ) {
                        *newline = '\0';
                }
                
-               retLen += strlen(buf+bufPos);
+               // Increment return length by amount of data up to newline (or end of read)
+               retLen += strlen(buf);
                ret = realloc(ret, retLen + 1);
-               strcat( ret, buf+bufPos );
-               
-               if( newline ) {
-                        int    newLen = newline - (buf+bufPos) + 1;
-                       bufValid = len - newLen;
-                       len = newLen;
-               }
-               if( len + bufPos == BUFSIZ - 1 )        bufPos = 0;
-               else    bufPos += len;
+               assert(ret);    // evil NULL check
+               strcat( ret, buf );     // append buffer data
        }
        
        #if DEBUG_TRACE_SERVER
        printf("%i '%s'\n", retLen, ret);
        #endif
+
+       // If the newline wasn't the last character in the buffer. (I.e. there's extra data for the next call)
+       assert(newline - buf + 1 <= len);
+       if( newline - buf + 1 < len ) {
+               int extra_bytes = len - (newline - buf + 1);
+               // Copy `extra_bytes` from end of buffer down to start and set `bufValid` to `extra_bytes`?
+               memmove(&buf[0], newline + 1, extra_bytes);
+               bufValid = extra_bytes;
+               
+               #if DEBUG_TRACE_SERVER > 1
+               printf("- Caching %i bytes '%.*s'\n", bufValid, bufValid, buf);
+               #endif
+       }
        
        return ret;
 }

UCC git Repository :: git.ucc.asn.au