[Redbutton-devel] SF.net SVN: redbutton: [7] redbutton-download/trunk
Brought to you by:
skilvington
|
From: <ski...@us...> - 2006-03-02 11:12:08
|
Revision: 7 Author: skilvington Date: 2006-03-02 03:11:58 -0800 (Thu, 02 Mar 2006) ViewCVS: http://svn.sourceforge.net/redbutton/?rev=7&view=rev Log Message: ----------- canonicalise ContentReferences passed to rb-download Modified Paths: -------------- redbutton-download/trunk/command.c redbutton-download/trunk/listen.h redbutton-download/trunk/rb-download.c Modified: redbutton-download/trunk/command.c =================================================================== --- redbutton-download/trunk/command.c 2006-03-01 16:32:29 UTC (rev 6) +++ redbutton-download/trunk/command.c 2006-03-02 11:11:58 UTC (rev 7) @@ -7,12 +7,14 @@ #include <stdbool.h> #include "command.h" +#include "fs.h" #include "utils.h" /* max number of args that can be passed to a command (arbitrary) */ #define ARGV_MAX 10 /* the commands */ +bool cmd_check(struct listen_data *, int, int, char **); bool cmd_file(struct listen_data *, int, int, char **); bool cmd_help(struct listen_data *, int, int, char **); bool cmd_quit(struct listen_data *, int, int, char **); @@ -25,13 +27,21 @@ char *help; } command[] = { - { "exit", "", cmd_quit, "Kill the programme" }, - { "file", "<ContentReference>", cmd_file, "Retrieve the given file from the carousel" }, - { "help", "", cmd_help, "List available commands" }, - { "quit", "", cmd_quit, "Kill the programme" }, + { "check", "<ContentReference>", cmd_check, "Check if the given file exists on the carousel" }, + { "exit", "", cmd_quit, "Kill the programme" }, + { "file", "<ContentReference>", cmd_file, "Retrieve the given file from the carousel" }, + { "help", "", cmd_help, "List available commands" }, + { "quit", "", cmd_quit, "Kill the programme" }, { NULL, NULL, NULL, NULL } }; +/* send an OK/error code etc response down client_sock */ +#define SEND_RESPONSE(RC, MESSAGE) write_string(client_sock, #RC " " MESSAGE "\n") + +/* internal routines */ +char *external_filename(struct listen_data *, char *); +char *canonical_filename(char *); + /* * process the given command * return true if we should quit the programme @@ -84,7 +94,7 @@ return (command[i].proc)(listen_data, client_sock, argc, argv); } - write_string(client_sock, "500 Unrecognised command\n"); + SEND_RESPONSE(500, "Unrecognised command"); return false; } @@ -98,7 +108,47 @@ * return true if we should quit the programme */ +#define CHECK_USAGE(ARGC, SYNTAX) \ +if(argc != ARGC) \ +{ \ + SEND_RESPONSE(500, "Syntax: " SYNTAX); \ + return false; \ +} + /* + * check <ContentReference> + * check if the given file is on the carousel + * ContentReference should be absolute, ie start with "~//" + */ + +bool +cmd_check(struct listen_data *listen_data, int client_sock, int argc, char *argv[]) +{ + char *filename; + FILE *file; + + CHECK_USAGE(2, "check <ContentReference>"); + + if((filename = external_filename(listen_data, argv[1])) == NULL) + { + SEND_RESPONSE(500, "Invalid ContentReference"); + return false; + } + + if((file = fopen(filename, "r")) != NULL) + { + fclose(file); + SEND_RESPONSE(200, "OK"); + } + else + { + SEND_RESPONSE(404, "Not found"); + } + + return false; +} + +/* * file <ContentReference> * send the given file down client_sock * ContentReference should be absolute, ie start with "~//" @@ -112,32 +162,23 @@ size_t nread; char buff[1024 * 8]; - if(argc != 2) - { - write_string(client_sock, "500 Syntax: file <ContentReference>\n"); - return false; - } + CHECK_USAGE(2, "file <ContentReference>"); - filename = argv[1]; - - /* is ContentReference absolute */ - if(strlen(filename) < 3 || strncmp(filename, "~//", 3) != 0) + if((filename = external_filename(listen_data, argv[1])) == NULL) { - write_string(client_sock, "500 ContentReference is not absolute\n"); + SEND_RESPONSE(500, "Invalid ContentReference"); return false; } - /* strip off the ~// prefix */ - filename += 3; - if((file = fopen(filename, "r")) == NULL) { - write_string(client_sock, "404 Not found\n"); + SEND_RESPONSE(404, "Not found"); return false; } - write_string(client_sock, "200 OK\n"); + SEND_RESPONSE(200, "OK"); + /* send the file contents */ do { nread = fread(buff, 1, sizeof(buff), file); @@ -161,7 +202,7 @@ char name_args[64]; char help_line[128]; - write_string(client_sock, "200 OK\n"); + SEND_RESPONSE(200, "OK"); for(i=0; command[i].name != NULL; i++) { @@ -183,3 +224,86 @@ return true; } +/* + * return a filename that can be used to load the given ContentReference from the filesystem + * returns a static string that will be overwritten by the next call to this routine + * returns NULL if the ContentReference is invalid (does not start with ~// or has too many .. components) + */ + +static char _external[PATH_MAX]; + +char * +external_filename(struct listen_data *listen_data, char *cref) +{ + char *canon_cref; + + /* is ContentReference absolute */ + if(strlen(cref) < 3 || strncmp(cref, "~//", 3) != 0) + return NULL; + + /* strip off the ~// prefix, and canonicalise the reference */ + canon_cref = canonical_filename(cref + 3); + + /* if the canonical name starts with "../", it is invalid */ + if(strncmp(canon_cref, "../", 3) == 0) + return NULL; + + /* create the carousel filename, ie prepend the servive gateway directory */ + snprintf(_external, sizeof(_external), "%s/%u/%s", SERVICES_DIR, listen_data->carousel->service_id, canon_cref); + + return _external; +} + +/* + * return a string that recursively removes all sequences of the form '/x/../' in path + * returns a static string that will be overwritten by the next call to this routine + */ + +static char _canon[PATH_MAX]; + +char * +canonical_filename(char *path) +{ + char *start; + char *slash; + size_t len; + + /* copy path into the output buffer */ + strncpy(_canon, path, sizeof(_canon)); + /* just in case */ + _canon[sizeof(_canon)-1] = '\0'; + + /* keep removing "/x/../" until there are none left */ + start = _canon; + while(true) + { + /* find the start of the first path component that is not "../" */ + while(strncmp(start, "../", 3) == 0) + start += 3; + /* find the next slash in the path */ + slash = start; + while(*slash != '/' && *slash != '\0') + slash ++; + /* no more slashes => nothing left to do */ + if(*slash == '\0') + return _canon; + /* if the next path component is "../", eat the previous one */ + if(strncmp(slash, "/../", 4) == 0) + { + /* include \0 terminator */ + len = strlen(start) + 1; + memmove(start, slash + 4, len - ((slash - start) + 4)); + /* restart the search */ + start = _canon; + } + else + { + /* move to the next component */ + start = slash + 1; + } + } + + /* not reached */ + return NULL; +} + Modified: redbutton-download/trunk/listen.h =================================================================== --- redbutton-download/trunk/listen.h 2006-03-01 16:32:29 UTC (rev 6) +++ redbutton-download/trunk/listen.h 2006-03-02 11:11:58 UTC (rev 7) @@ -7,9 +7,12 @@ #include <netinet/in.h> +#include "module.h" + struct listen_data { struct sockaddr_in addr; /* ip:port to listen on */ + struct carousel *carousel; /* carousel we are downloading */ }; int parse_addr(char *, struct in_addr *, in_port_t *); Modified: redbutton-download/trunk/rb-download.c =================================================================== --- redbutton-download/trunk/rb-download.c 2006-03-01 16:32:29 UTC (rev 6) +++ redbutton-download/trunk/rb-download.c 2006-03-02 11:11:58 UTC (rev 7) @@ -132,7 +132,10 @@ car = find_mheg(device, timeout, service_id, carousel_id); printf("Carousel ID=%u\n", car->carousel_id); if(listen) + { + listen_data.carousel = car; start_listener(&listen_data); + } load_carousel(car); } else This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |