Added upload authentication

svn path=/trunk/boinc/; revision=162
This commit is contained in:
David Anderson 2002-07-05 05:33:40 +00:00
parent 7e608fd1a8
commit 316de23144
57 changed files with 1121 additions and 255 deletions

View File

@ -10,34 +10,34 @@ VPATH = @srcdir@
# This needs to be fixed
#
all:
cd db; gmake; cd ..; \
cd RSAEuro/source/; gmake; cd ../../; \
cd lib; gmake; cd ..; \
cd api; gmake; cd ..; \
cd apps; gmake; cd ..; \
cd client; ../$(srcdir)/client/configure; gmake; cd ..; \
cd sched; gmake; cd ..; \
cd tools; gmake; cd ..;
cd RSAEuro/source; make; cd ../..; \
cd db; make; cd ..; \
cd lib; make; cd ..; \
cd api; make; cd ..; \
cd apps; make; cd ..; \
cd client; ../$(srcdir)/client/configure; make; cd ..; \
cd sched; make; cd ..; \
cd tools; make; cd ..;
clean:
rm -f boinc.tar.gz config.cache; \
cd db; gmake clean; cd ..; \
cd RSAEuro/source/; gmake clean; cd ../../; \
cd lib; gmake clean; cd ..; \
cd api; gmake clean; cd ..; \
cd apps; gmake clean; cd ..; \
cd client; gmake clean; cd ..; \
cd sched; gmake clean; cd ..; \
cd tools; gmake clean; cd ..;
cd RSAEuro/source/; make clean; cd ../../; \
cd db; make clean; cd ..; \
cd lib; make clean; cd ..; \
cd api; make clean; cd ..; \
cd apps; make clean; cd ..; \
cd client; make clean; cd ..; \
cd sched; make clean; cd ..; \
cd tools; make clean; cd ..;
install: all
-mkdir -p /usr/local/boinc;
cd sched; gmake install;
cd lib; gmake install;
cd api; gmake install;
cd apps; gmake install;
cd client; gmake install;
cd tools; gmake install;
cd sched; make install;
cd lib; make install;
cd api; make install;
cd apps; make install;
cd client; make install;
cd tools; make install;
cp -r test /usr/local/boinc;
tar: clean

14
TODO
View File

@ -1,13 +1,14 @@
HIGH-PRIORITY (must be done to support SETI@home)
- Code-signing (David)
research tools for code-signing
- Code-signing
In progress - David
- Upload authentication (David)
Each result contains a "certificate", signed with project key, giving
- list of: file name, max size
- min, max times to xfer
modify put program to decrypt certificate, enforce name/size/time limits
In progress - David
- Network retry policies (Eric?)
can't download file: when to give up? how to retry?
@ -16,18 +17,24 @@ HIGH-PRIORITY (must be done to support SETI@home)
can't connect to sched server
error return from sched server
- proxy support (Open Source)
- make scheduling server use fast CGI
In progress - Michael
- proxy support
HTTP, Socks
Look at other open source code
- team system (Barry)
in PHP
In progress - Barry
- credit display (Barry)
in PHP
In progress - Barry
- CPU accounting in the presence of checkpoint/restart (Michael)
core client periodically gets CPU time, accumulates in state file
In progress - Michael
- test versioning mechanisms for core
Idea: need to notify user if core becomes out of date.
@ -45,6 +52,7 @@ HIGH-PRIORITY (must be done to support SETI@home)
- initialize rsc_fpops and rsc_iops in client WORKUNIT
- check server sends correct number of work units
- check client requests correct number of seconds of work
In progress - Michael
- measure hardware parameters: CPU speed, #CPUs, memory, disk
- define CPU benchmarks

View File

@ -575,6 +575,18 @@ David A June 20 2002
prefs1.xml, prefs2.xml (new)
test_*.php
David A June 21 2002
- top-level Makefile now compiles RSAEuro/,
and doesn't refer to sched_fcgi
- Added <scheduler> element to html_user/index.html,
making it the "master file" for test project.
This file must be placed in the directory referred to by
http://localhost/
Makefile.in
html_user/
index.html
Michael Gary June 21 2002
- added install to the make system to put executables
in /usr/local/boinc
@ -732,4 +744,103 @@ Michael Gary 7/03/2002
configure
configure.in
David Anderson July 4, 2002
- Added support for upload authentication.
This prevents bad guys from filling up data servers with trash.
In this scheme, each <file_info> element sent from
server to client includes a <max_nbytes> field limiting
the size of the file, and includes a digital signature
based on the project's "upload authentication" key pair.
File uploads, instead of being done by PUT, are now done by POST
to a CGI program, "file_upload_handler".
The request header includes the signed <file_info>,
and the CGI program verifies the signature and enforces the size limit.
The affected pieces of code:
- Added a function create_keys() in PHP test scripts
to create encryption keys. Call it from all script.
- Added environment var BOINC_KEY_DIR saying where keys are kept.
- The client must maintain an exact copy of each <file_info> XML,
and of the signature, so that it can send to upload server.
- Added a new variant of HTTP operation, HTTP_OP_POST2.
The existing variants all use single files for request and reply.
The new variant (used for file upload) has a request
consisting of a memory block followed by (part of) a file;
the reply is in memory.
This avoid copying possibly huge upload files.
- FILE_XFER objects now take a FILE_INFO as initialization argument;
needed to convey authentication info.
The upload variant creates and sends the authentication header.
- Result templates now include a <max_nbytes> in each
<file_info> element, and the URLs refer to the
file_upload_handler (with no filename)
- process_result_template() works differently, since it must
generate a digital signature at the end of each <file_info>
- create_work expects the name of a private key file.
- Added crypt/md5 functions to sign/verify in memory,
encode/decode ASCII data in memory, checksum in memory
- Change "gmake" to "make" in top-level makefile.
(alias make to gmake if this is a problem)
boinc/
Makefile.in
TODO
RSAEuro/source/
rsaeuro.h
client/
Makefile.in
client_state.C
client_types.C,h
cs_files.C
file_names.C
file_xfer.C,h
http.C,h
main.C
scheduler_op.C
test_file_xfer.C
db/
mysql_util.C
doc/
index.html
intro.html
master_url.html (new)
project_startup.html (new)
tools_security.html (new)
html_user/
index.html
lib/
Makefile.in
crypt.C,h
crypt_prog.C
md5_file.C,h
parse.C,h
sched/
Makefile.in
file_upload_handler.C
server_types.C
test/
1sec_result
concat_result
init.inc
master.html (new)
sah_result
test_1sec.php
test_concat.php
test_dynamic.php
test_max_water_prefs.php
test_min_water_prefs.php
test_normal_water_prefs.php
test_prefs.php
test_projects.php
test_stderr.php
test_uc.php
test_uc_slow.php
uc_result
ucs_result
tools/
Makefile.in
add.C
backend_lib.C,h
create_work.C
process_result_template.C

View File

@ -56,6 +56,8 @@ TEST_HTTP_OBJS = $(TEST_NET_XFER_OBJS)
TEST_FX_OBJS = \
$(TEST_HTTP_OBJS) \
client_types.o \
file_names.o \
file_xfer.o
.C.o:

View File

@ -195,7 +195,7 @@ int CLIENT_STATE::parse_state_file() {
}
} else if (match_tag(buf, "<file_info>")) {
FILE_INFO* fip = new FILE_INFO;
fip->parse(f);
fip->parse(f, false);
if (project) {
retval = link_file_info(project, fip);
if (!retval) file_infos.push_back(fip);
@ -222,7 +222,7 @@ int CLIENT_STATE::parse_state_file() {
}
} else if (match_tag(buf, "<result>")) {
RESULT* rp = new RESULT;
rp->parse(f, "</result>");
rp->parse_state(f);
if (project) {
retval = link_result(project, rp);
if (!retval) results.push_back(rp);

View File

@ -174,14 +174,17 @@ FILE_INFO::FILE_INFO() {
FILE_INFO::~FILE_INFO() {
}
int FILE_INFO::parse(FILE* in) {
// If from server, make an exact copy of everything
// except the start/end tags and the signature element.
//
int FILE_INFO::parse(FILE* in, bool from_server) {
char buf[256];
STRING256 url;
strcpy(name, "");
//strcpy(url, "");
strcpy(md5_cksum, "");
nbytes = 0;
max_nbytes = 0;
generated_locally = false;
file_present = false;
executable = false;
@ -191,21 +194,39 @@ int FILE_INFO::parse(FILE* in) {
project = NULL;
file_xfer = NULL;
urls.clear();
if (from_server) {
signed_xml = strdup("");
} else {
signed_xml = 0;
}
signature = 0;
while (fgets(buf, 256, in)) {
if (match_tag(buf, "</file_info>")) return 0;
else if (parse_str(buf, "<name>", name)) continue;
else if (match_tag(buf, "<signature>")) {
dup_element_contents(in, "</signature>", &signature);
continue;
}
if (from_server) {
strcatdup(signed_xml, buf);
}
if (parse_str(buf, "<name>", name)) continue;
else if (parse_str(buf, "<url>", url.text)) {
urls.push_back(url);
continue;
}
else if (parse_str(buf, "<md5_cksum>", md5_cksum)) continue;
else if (parse_double(buf, "<nbytes>", nbytes)) continue;
else if (parse_double(buf, "<max_nbytes>", max_nbytes)) continue;
else if (match_tag(buf, "<generated_locally/>")) generated_locally = true;
else if (match_tag(buf, "<file_present/>")) file_present = true;
else if (match_tag(buf, "<executable/>")) executable = true;
else if (match_tag(buf, "<uploaded/>")) uploaded = true;
else if (match_tag(buf, "<upload_when_present/>")) upload_when_present = true;
else if (match_tag(buf, "<sticky/>")) sticky = true;
else if (!from_server && match_tag(buf, "<signed_xml>")) {
dup_element_contents(in, "</signed_xml>", &signed_xml);
continue;
}
else fprintf(stderr, "FILE_INFO::parse(): unrecognized: %s\n", buf);
}
return 1;
@ -217,8 +238,9 @@ int FILE_INFO::write(FILE* out, bool to_server) {
"<file_info>\n"
" <name>%s</name>\n"
" <md5_cksum>%s</md5_cksum>\n"
" <nbytes>%f</nbytes>\n",
name, md5_cksum, nbytes
" <nbytes>%f</nbytes>\n"
" <max_nbytes>%f</max_nbytes>\n",
name, md5_cksum, nbytes, max_nbytes
);
if (!to_server) {
if (generated_locally) fprintf(out, " <generated_locally/>\n");
@ -231,6 +253,14 @@ int FILE_INFO::write(FILE* out, bool to_server) {
for (i=0; i<urls.size(); i++) {
fprintf(out, "<url>%s</url>\n", urls[i].text);
}
if (!to_server) {
if (signed_xml) {
fprintf(out, "<signed_xml>\n%s</signed_xml>\n", signed_xml);
}
if (signature) {
fprintf(out, "<signature>\n%s</signature>\n", signature);
}
}
fprintf(out, "</file_info>\n");
return 0;
}
@ -369,26 +399,63 @@ int WORKUNIT::write(FILE* out) {
return 0;
}
int RESULT::parse(FILE* in, char* end_tag) {
int RESULT::parse_ack(FILE* in) {
char buf[256];
FILE_REF file_ref;
strcpy(name, "");
while (fgets(buf, 256, in)) {
if (match_tag(buf, "</result_ack>")) return 0;
else if (parse_str(buf, "<name>", name)) continue;
else fprintf(stderr, "RESULT::parse(): unrecognized: %s\n", buf);
}
return 1;
}
void RESULT::clear() {
strcpy(name, "");
strcpy(wu_name, "");
strcpy(stderr_out, "");
output_files.clear();
is_active = false;
is_compute_done = false;
is_server_ack = false;
cpu_time = 0;
exit_status = 0;
strcpy(stderr_out, "");
app = NULL;
wup = NULL;
project = NULL;
}
// parse a <result> element from scheduling server.
//
int RESULT::parse_server(FILE* in) {
char buf[256];
FILE_REF file_ref;
while (fgets(buf, 256, in)) {
if (match_tag(buf, end_tag)) return 0;
else if (parse_str(buf, "<name>", name)) continue;
else if (parse_str(buf, "<wu_name>", wu_name)) continue;
else if (match_tag(buf, "<file_ref>")) {
if (match_tag(buf, "</result>")) return 0;
if (parse_str(buf, "<name>", name)) continue;
if (parse_str(buf, "<wu_name>", wu_name)) continue;
if (match_tag(buf, "<file_ref>")) {
file_ref.parse(in);
output_files.push_back(file_ref);
continue;
}
else fprintf(stderr, "RESULT::parse(): unrecognized: %s\n", buf);
}
return 1;
}
// parse a <result> element from state file
//
int RESULT::parse_state(FILE* in) {
char buf[256];
FILE_REF file_ref;
while (fgets(buf, 256, in)) {
if (match_tag(buf, "</result>")) return 0;
if (parse_str(buf, "<name>", name)) continue;
if (parse_str(buf, "<wu_name>", wu_name)) continue;
if (match_tag(buf, "<file_ref>")) {
file_ref.parse(in);
output_files.push_back(file_ref);
continue;

View File

@ -35,6 +35,7 @@
#define STDERR_MAX_LEN 4096
class FILE_XFER;
class RESULT;
struct STRING256 {
char text[256];
@ -85,8 +86,8 @@ struct APP {
class FILE_INFO {
public:
char name[256];
//char url[256]; // TODO: allow multiple URLs
char md5_cksum[33];
double max_nbytes;
double nbytes;
bool generated_locally; // file is produced by app
bool file_present;
@ -95,13 +96,16 @@ public:
bool upload_when_present;
bool sticky; // don't delete unless instructed to do so
FILE_XFER* file_xfer; // nonzero if in the process of being up/downloaded
RESULT* result; // for upload files (to authenticate)
PROJECT* project;
int ref_cnt;
vector<STRING256> urls;
char* signed_xml;
char* signature;
FILE_INFO();
~FILE_INFO();
int parse(FILE*);
int parse(FILE*, bool from_server);
int write(FILE*, bool to_server);
int delete_file(); // attempt to delete the underlying file
};
@ -168,7 +172,10 @@ struct RESULT {
WORKUNIT* wup;
PROJECT* project;
int parse(FILE*, char* end_tag);
void clear();
int parse_server(FILE*);
int parse_state(FILE*);
int parse_ack(FILE*);
int write(FILE*, bool to_server);
bool is_upload_done(); // files uploaded?
};

View File

@ -54,8 +54,8 @@ bool CLIENT_STATE::start_file_xfers() {
fxp = fip->file_xfer;
if (!fip->generated_locally && !fip->file_present && !fxp) {
fxp = new FILE_XFER;
get_pathname(fip, pathname);
fxp->init_download(fip->urls[0].text, pathname);
//get_pathname(fip, pathname);
fxp->init_download(*fip);
retval = file_xfers->insert(fxp);
if (retval) {
fprintf(stderr,
@ -66,8 +66,8 @@ bool CLIENT_STATE::start_file_xfers() {
fip->file_xfer = fxp;
if (log_flags.file_xfer) {
printf(
"started download of %s to %s\n",
fip->urls[0].text, pathname
"started download of %s\n",
fip->urls[0].text
);
}
}
@ -77,8 +77,7 @@ bool CLIENT_STATE::start_file_xfers() {
&& !fip->uploaded && !fxp
) {
fxp = new FILE_XFER;
get_pathname(fip, pathname);
fxp->init_upload( fip->urls[0].text, pathname);
fxp->init_upload(*fip);
retval = file_xfers->insert(fxp);
if (retval) {
fprintf(stderr,
@ -88,10 +87,7 @@ bool CLIENT_STATE::start_file_xfers() {
} else {
fip->file_xfer = fxp;
if (log_flags.file_xfer) {
printf(
"started upload of %s to %s\n",
pathname, fip->urls[0].text
);
printf("started upload to %s\n", fip->urls[0].text);
}
}
action = true;

View File

@ -71,8 +71,15 @@ void get_pathname(FILE_INFO* fip, char* path) {
PROJECT* p = fip->project;
char buf[256];
escape_url(p->master_url, buf);
sprintf(path, "%s/%s", buf, fip->name);
// for testing purposes, it's handy to allow a FILE_INFO without
// an associated PROJECT.
//
if (p) {
escape_url(p->master_url, buf);
sprintf(path, "%s/%s", buf, fip->name);
} else {
strcpy(path, fip->name);
}
}
void get_slot_dir(int slot, char* path) {

View File

@ -20,6 +20,7 @@
#include "windows_cpp.h"
#include "util.h"
#include "file_names.h"
#include "log_flags.h"
#include "file_xfer.h"
@ -31,6 +32,7 @@ FILE_XFER::FILE_XFER() {
FILE_XFER::~FILE_XFER() {
}
#if 0
int FILE_XFER::init_download(char* url, char* outfile) {
return HTTP_OP::init_get(url, outfile);
}
@ -38,6 +40,37 @@ int FILE_XFER::init_download(char* url, char* outfile) {
int FILE_XFER::init_upload(char* url, char* infile) {
return HTTP_OP::init_put(url, infile);
}
#endif
int FILE_XFER::init_download(FILE_INFO& file_info) {
fip = &file_info;
get_pathname(fip, pathname);
return HTTP_OP::init_get((char*)(&fip->urls[0]), pathname);
}
// for uploads, we need to build a header with signature etc.
// (see file_upload_handler.C for a spec)
// Do this in memory.
//
int FILE_XFER::init_upload(FILE_INFO& file_info) {
fip = &file_info;
get_pathname(fip, pathname);
sprintf(header,
"<file_info>\n"
"%s"
"<signature>\n"
"%s"
"</signature>\n"
"</file_info>\n"
"<nbytes>%f</nbytes>\n"
"<offset>0</offset>\n"
"<data>\n",
file_info.signed_xml,
file_info.signature,
file_info.nbytes
);
return HTTP_OP::init_post2((char*)(&fip->urls[0]), header, pathname, 0);
}
double FILE_XFER::elapsed_time() {
return end_time - start_time;

View File

@ -20,9 +20,14 @@
#ifndef _FILE_XFER_
#define _FILE_XFER_
// FILE_XFER objects encapsulate the transfer of a file
// to/from a particular server.
// TODO: use the HTTP Range header fields to do partial xfers
// FILE_XFER objects encapsulate the transfer of a file to/from data servers.
// In particular it manages:
// - the choice of data servers
// TODO: try servers beyond the first one
// - the retry and give-up policies
// TODO: retry and eventually give up
// - restarting partial transfers
// - upload authentication
#include "client_types.h"
#include "http.h"
@ -32,12 +37,17 @@ public:
double start_time;
double end_time;
FILE_INFO* fip;
char pathname[256];
char header[4096];
int state;
FILE_XFER();
~FILE_XFER();
int init_download(char* url, char* outfile);
int init_upload(char* url, char* infile);
//int init_download(char* url, char* outfile);
//int init_upload(char* url, char* infile);
int init_download(FILE_INFO&);
int init_upload(FILE_INFO&);
bool file_xfer_done;
int file_xfer_retval;
double elapsed_time();

View File

@ -21,6 +21,7 @@
#include <string.h>
#include <sys/stat.h>
#include <sys/socket.h>
#ifdef _WIN32
#include "winsock.h"
@ -35,7 +36,7 @@
#define HTTP_BLOCKSIZE 4096
void parse_url(char* url, char* host, char* file) {
static void parse_url(char* url, char* host, char* file) {
char* p;
char buf[256];
@ -55,7 +56,9 @@ void parse_url(char* url, char* host, char* file) {
// We use 1.0 so we don't have to count bytes.
//
void http_get_request_header(char* buf, char* host, char* file, int offset) {
static void http_get_request_header(
char* buf, char* host, char* file, int offset
) {
if (offset) {
sprintf(buf,
"GET /%s;byte-range %d- HTTP/1.0\015\012"
@ -79,7 +82,7 @@ void http_get_request_header(char* buf, char* host, char* file, int offset) {
}
}
void http_head_request_header(char* buf, char* host, char* file) {
static void http_head_request_header(char* buf, char* host, char* file) {
sprintf(buf,
"HEAD /%s HTTP/1.0\015\012"
"User-Agent: BOINC client\015\012"
@ -90,7 +93,9 @@ void http_head_request_header(char* buf, char* host, char* file) {
);
}
void http_post_request_header(char* buf, char* host, char* file, int size) {
static void http_post_request_header(
char* buf, char* host, char* file, int size
) {
sprintf(buf,
"POST /%s HTTP/1.0\015\012"
"Pragma: no-cache\015\012"
@ -103,6 +108,7 @@ void http_post_request_header(char* buf, char* host, char* file, int size) {
);
}
#if 0
void http_put_request_header(
char* buf, char* host, char* file, int size, int offset
) {
@ -132,20 +138,17 @@ void http_put_request_header(
);
}
}
#endif
int read_http_reply_header(int socket, HTTP_REPLY_HEADER& header) {
int i;
int i, n;
char buf[1024], *p;
memset(buf, 0, sizeof(buf));
header.content_length = 0;
header.status = 404; // default to failure
for (i=0; i<1024; i++) {
#ifdef _WIN32
recv(socket, buf+i, 1, 0);
#else
read(socket, buf+i, 1);
#endif
n = recv(socket, buf+i, 1, 0);
if (strstr(buf, "\r\n\r\n") || strstr(buf, "\n\n")) {
if (log_flags.http_debug) printf("reply header:\n%s", buf);
p = strchr(buf, ' ');
@ -162,6 +165,16 @@ int read_http_reply_header(int socket, HTTP_REPLY_HEADER& header) {
return 1;
}
static int read_reply(int socket, char* buf, int len) {
int i, n;
for (i=0; i<len-1; i++) {
n = recv(socket, buf+i, 1, 0);
if (n != 1) break;
}
buf[i] = 0;
return 0;
}
HTTP_OP::HTTP_OP() {
http_op_state = HTTP_STATE_IDLE;
}
@ -174,16 +187,18 @@ int HTTP_OP::init_head(char* url) {
NET_XFER::init(hostname, 80, HTTP_BLOCKSIZE);
http_op_type = HTTP_OP_HEAD;
http_op_state = HTTP_STATE_CONNECTING;
http_head_request_header(request_header, hostname, filename);
return 0;
}
int HTTP_OP::init_get(char* url, char* out, int off) {
offset = off;
file_offset = off;
parse_url(url, hostname, filename);
NET_XFER::init(hostname, 80, HTTP_BLOCKSIZE);
strcpy(outfile, out);
http_op_type = HTTP_OP_GET;
http_op_state = HTTP_STATE_CONNECTING;
http_get_request_header(request_header, hostname, filename, (int)file_offset);
return 0;
}
@ -198,9 +213,35 @@ int HTTP_OP::init_post(char* url, char* in, char* out) {
if (retval) return retval;
http_op_type = HTTP_OP_POST;
http_op_state = HTTP_STATE_CONNECTING;
http_post_request_header(
request_header, hostname, filename, content_length
);
return 0;
}
int HTTP_OP::init_post2(
char* url, char* r1, char* in, double offset
) {
int retval;
parse_url(url, hostname, filename);
NET_XFER::init(hostname, 80, HTTP_BLOCKSIZE);
req1 = r1;
strcpy(infile, in);
file_offset = offset;
retval = file_size(infile, content_length);
if (retval) return retval;
content_length -= (int)offset;
content_length += strlen(req1);
http_op_type = HTTP_OP_POST2;
http_op_state = HTTP_STATE_CONNECTING;
http_post_request_header(
request_header, hostname, filename, content_length
);
return 0;
}
#if 0
int HTTP_OP::init_put(char* url, char* in, int off) {
int retval;
@ -212,8 +253,12 @@ int HTTP_OP::init_put(char* url, char* in, int off) {
if (retval) return retval;
http_op_type = HTTP_OP_PUT;
http_op_state = HTTP_STATE_CONNECTING;
http_put_request_header(
request_header, hostname, filename, content_length, offset
);
return 0;
}
#endif
bool HTTP_OP::http_op_done() {
return (http_op_state == HTTP_STATE_DONE);
@ -233,7 +278,6 @@ int HTTP_OP_SET::insert(HTTP_OP* ho) {
bool HTTP_OP_SET::poll() {
unsigned int i;
char hdr[256];
HTTP_OP* htp;
int n;
bool action = false;
@ -251,36 +295,14 @@ bool HTTP_OP_SET::poll() {
case HTTP_STATE_REQUEST_HEADER:
if (htp->io_ready) {
action = true;
switch(htp->http_op_type) {
case HTTP_OP_POST:
http_post_request_header(
hdr, htp->hostname, htp->filename, htp->content_length
);
break;
case HTTP_OP_GET:
http_get_request_header(hdr, htp->hostname, htp->filename, htp->offset);
break;
case HTTP_OP_HEAD:
http_head_request_header(hdr, htp->hostname, htp->filename);
break;
case HTTP_OP_PUT:
http_put_request_header(
hdr, htp->hostname, htp->filename, htp->content_length, htp->offset
);
break;
}
#ifdef _WIN32
n = send(htp->socket, hdr, strlen(hdr), 0);
#else
n = write(htp->socket, hdr, strlen(hdr));
#endif
n = send(htp->socket, htp->request_header, strlen(htp->request_header), 0);
if (log_flags.http_debug) {
printf("wrote HTTP header: %d bytes\n", n);
}
htp->io_ready = false;
switch(htp->http_op_type) {
case HTTP_OP_POST:
case HTTP_OP_PUT:
//case HTTP_OP_PUT:
htp->http_op_state = HTTP_STATE_REQUEST_BODY;
htp->file = fopen(htp->infile, "r");
if (!htp->file) {
@ -298,9 +320,30 @@ bool HTTP_OP_SET::poll() {
htp->want_upload = false;
htp->want_download = true;
break;
case HTTP_OP_POST2:
htp->http_op_state = HTTP_STATE_REQUEST_BODY1;
break;
}
}
break;
case HTTP_STATE_REQUEST_BODY1:
if (htp->io_ready) {
action = true;
n = send(htp->socket, htp->req1, strlen(htp->req1), 0);
htp->http_op_state = HTTP_STATE_REQUEST_BODY;
htp->file = fopen(htp->infile, "r");
if (!htp->file) {
fprintf(stderr, "HTTP_OP: no input file %s\n", htp->infile);
htp->io_done = true;
htp->http_op_retval = ERR_FOPEN;
htp->http_op_state = HTTP_STATE_DONE;
break;
}
fseek(htp->file, (long)htp->file_offset, SEEK_SET);
htp->io_ready = false;
htp->do_file_io = true;
}
break;
case HTTP_STATE_REQUEST_BODY:
if (htp->io_done) {
action = true;
@ -313,6 +356,7 @@ bool HTTP_OP_SET::poll() {
htp->do_file_io = false;
htp->want_upload = false;
htp->want_download = true;
htp->io_ready = false;
}
case HTTP_STATE_REPLY_HEADER:
if (htp->io_ready) {
@ -349,22 +393,35 @@ bool HTTP_OP_SET::poll() {
}
htp->do_file_io = true;
break;
case HTTP_OP_POST2:
htp->http_op_state = HTTP_STATE_REPLY_BODY;
htp->io_ready = false;
break;
#if 0
case HTTP_OP_PUT:
htp->http_op_state = HTTP_STATE_DONE;
htp->http_op_retval = 0;
#endif
}
}
break;
case HTTP_STATE_REPLY_BODY:
if (htp->io_done) {
action = true;
switch(htp->http_op_type) {
case HTTP_OP_POST2:
read_reply(htp->socket, htp->req1, 256);
break;
default:
action = true;
fclose(htp->file);
htp->file = 0;
break;
}
if (log_flags.http_debug) printf("got reply body\n");
fclose(htp->file);
htp->file = 0;
htp->http_op_state = HTTP_STATE_DONE;
htp->http_op_retval = 0;
break;
}
break;
}
}
return action;

View File

@ -17,6 +17,10 @@
// Contributor(s):
//
// HTTP_OP represents an HTTP operation.
// There are variants for GET, POST etc.
// as well as for the data source/sink.
#ifndef _HTTP_
#define _HTTP_
@ -27,13 +31,16 @@ struct HTTP_REPLY_HEADER {
int content_length;
};
// For the first 4, data source/sink are files
#define HTTP_OP_GET 1
#define HTTP_OP_POST 2
#define HTTP_OP_PUT 3
//#define HTTP_OP_PUT 3
#define HTTP_OP_HEAD 4
#define HTTP_OP_POST2 5
// a POST operation where the request comes from a combination
// of a string and a file w/offset,
// and the reply goes into a memory buffer
// represents an HTTP request in progress
//
class HTTP_OP : public NET_XFER {
public:
HTTP_OP();
@ -41,10 +48,12 @@ public:
char hostname[256];
char filename[256];
char* req1;
char infile[256];
char outfile[256];
int content_length;
int offset;
double file_offset;
char request_header[256];
HTTP_REPLY_HEADER hrh;
int http_op_state; // values below
int http_op_type;
@ -53,7 +62,10 @@ public:
int init_head(char* url);
int init_get(char* url, char* outfile, int offset=0);
int init_post(char* url, char* infile, char* outfile);
int init_put(char* url, char* infile, int offset=0);
int init_post2(
char* url, char* req1, char* infile, double offset
);
//int init_put(char* url, char* infile, int offset=0);
bool http_op_done();
};
@ -73,10 +85,11 @@ public:
#define HTTP_STATE_IDLE 0
#define HTTP_STATE_CONNECTING 1
#define HTTP_STATE_REQUEST_HEADER 2
#define HTTP_STATE_REQUEST_BODY 3
#define HTTP_STATE_REPLY_HEADER 4
#define HTTP_STATE_REPLY_BODY 5
#define HTTP_STATE_DONE 6
#define HTTP_STATE_REQUEST_BODY1 3
#define HTTP_STATE_REQUEST_BODY 4
#define HTTP_STATE_REPLY_HEADER 5
#define HTTP_STATE_REPLY_BODY 6
#define HTTP_STATE_DONE 7
extern int read_http_reply_header(int socket, HTTP_REPLY_HEADER&);

View File

@ -104,6 +104,7 @@ int main(int argc, char** argv) {
boinc_sleep(1);
}
if (cs.time_to_exit()) {
printf("time to exit\n");
break;
}
}

View File

@ -162,7 +162,7 @@ int SCHEDULER_REPLY::parse(FILE* in) {
apps.push_back(app);
} else if (match_tag(buf, "<file_info>")) {
FILE_INFO file_info;
file_info.parse(in);
file_info.parse(in, true);
file_infos.push_back(file_info);
} else if (match_tag(buf, "<app_version>")) {
APP_VERSION av;
@ -175,11 +175,11 @@ int SCHEDULER_REPLY::parse(FILE* in) {
} else if (match_tag(buf, "<result>")) {
RESULT result; // make sure this is here so constructor
// gets called each time
result.parse(in, "</result>");
result.parse_server(in);
results.push_back(result);
} else if (match_tag(buf, "<result_ack>")) {
RESULT result;
result.parse(in, "</result_ack>");
result.parse_ack(in);
result_acks.push_back(result);
} else if (parse_str(buf, "<message", message)) {
parse_attr(buf, "priority", message_priority);

View File

@ -25,38 +25,69 @@
#include "util.h"
#define DOWNLOAD_URL "http://localhost.localdomain/download/input"
#define UPLOAD_URL "http://localhost.localdomain/upload/test_out.html"
#define UPLOAD_URL "http://localhost.localdomain/boinc-cgi/file_upload_handler"
int main() {
NET_XFER_SET nxs;
HTTP_OP_SET hos(&nxs);
FILE_XFER_SET fxs(&hos);
int retval, n;
FILE_XFER* fx1=0, *fx2 = 0;
bool do_upload = true;
bool do_download = false;
FILE_INFO fi1, fi2;
STRING256 str;
FILE_XFER* fx1 = new FILE_XFER;
retval = fx1->init_download(DOWNLOAD_URL, "test_fx_out");
if (retval) {
printf("init_download failed\n");
exit(1);
}
FILE_XFER* fx2= new FILE_XFER;
retval = fx2->init_upload(UPLOAD_URL, "test_fx_in");
if (retval) {
printf("init_upload failed\n");
exit(1);
if (do_download) {
fx1 = new FILE_XFER;
memset(&fi1, 0, sizeof(fi1));
strcpy(fi1.name, "test_fx_out");
strcpy(str.text, DOWNLOAD_URL);
fi1.urls.push_back(str);
retval = fx1->init_download(fi1);
if (retval) {
printf("init_download failed\n");
exit(1);
}
retval = fxs.insert(fx1);
if (retval) {
printf("insert failed\n");
exit(1);
}
}
retval = fxs.insert(fx1);
if (retval) {
printf("insert failed\n");
exit(1);
}
retval = fxs.insert(fx2);
if (retval) {
printf("insert failed\n");
exit(1);
if (do_upload) {
fx2= new FILE_XFER;
memset(&fi2, 0, sizeof(fi1));
strcpy(fi2.name, "test_fx_in");
strcpy(str.text, UPLOAD_URL);
fi2.urls.push_back(str);
fi2.signed_xml = \
" <name>uc_wu_1_0</name>\n"
" <generated_locally/>\n"
" <upload_when_present/>\n"
" <max_nbytes>10000</max_nbytes>\n"
" <url>http://localhost/upload/uc_wu_1_0</url>\n";
fi2.signature = \
"9d1f8152371c67af1d26b25db104014dbf7e9ad3b61fc8334ee06e01c7529b1a\n"
"7681c3e7c7828525361a01040d1197147286085231ee5d2554e59ecb40b3e6a5\n"
"afbaf00ff15bc5b1acf5aa6318bc84f2671a9502ada9c2ce37a9c45480a0e3b7\n"
"b3dcb6c3bf09feaebc81b76063ef12b0031cf041eaef811166839533067b74f6\n"
".\n";
retval = fx2->init_upload(fi2);
if (retval) {
printf("init_upload failed\n");
exit(1);
}
retval = fxs.insert(fx2);
if (retval) {
printf("insert failed\n");
exit(1);
}
}
while (1) {
nxs.poll(100000, n);
hos.poll();

View File

@ -36,7 +36,7 @@ static MYSQL *mp;
static MYSQL_RES *rp;
static MYSQL_ROW row;
#define MAX_QUERY_LEN 4096
#define MAX_QUERY_LEN 8192
int db_open(char* name) {
mp = mysql_init(0);

View File

@ -19,16 +19,18 @@
<li><a href=back_end.html>Back end examples</a>
<li><a href=version.html>Versioning</a>
<li><a href=api.html>The BOINC application library</a>
<li><a href=graphics.html>Graphics</a>
<li><a href=graphics.html>Client graphics</a>
<li><a href=dev.html>Application development</a>
</ul>
<h3>Operating a BOINC project</h3>
<ul>
<li><a href=install.html>Installing BOINC</a>
<li><a href=project_startup.html>Project startup</a>
<li><a href=master_url.html>The master URL</a>
<li><a href=project_startup.html>Project startup checklist</a>
<li><a href=database.html>The BOINC database</a>
<li><a href=sched_server.html>The BOINC scheduling server</a>
<li><a href=tools_security.html>Operational tools: security</a>
<li><a href=tools_other.html>Operational tools: applications and versions</a>
<li><a href=tools_work.html>Operational tools: work and results</a>
<li><a href=web.html>The project web site</a>

View File

@ -12,7 +12,7 @@ The features of BOINC include:
<ul>
<li> BOINC allows multiple independent projects
to share a common set of participants.
to share participants.
Participants download a single <b>core client</b> program,
which in turn downloads and executes project-specific executables.
Participants can control how their resources are divided among the projects.
@ -28,14 +28,18 @@ Work is dispatched only to hosts able to handle it.
<li> BOINC applications can be developed in any language (C++, Fortran, Perl).
An application can consist of several files
(e.g. several programs and a coordinating script).
New versions of applications can be released without participant download.
(e.g. multiple programs and a coordinating script).
New versions of applications can be deployed without participant download.
Separate alpha, beta, and production versions
are distributed to the appropriate set of hosts.
<li> The BOINC core client can run on almost any platform
(Mac, Windows, Linux and other Unix systems).
<li> A BOINC project must provide and maintain its own server systems,
but these systems can be set up easily and involve
only open-source components (MySQL, PHP, Apache, and Linux).
<li> BOINC is distributed under the Mozilla license.
</ul>

27
doc/master_url.html Normal file
View File

@ -0,0 +1,27 @@
<h2>The master URL</h2>
<p>
Each project is identified by a <b>master URL</b>.
This URL is used to publicize the project.
The <b>master page</b> at this URL has two functions.
<ul>
<li>
It is the home page of the project;
when viewed in a browser it describes the project
and contains links for registering
and for downloading the core client.
<li>
It contains XML tags of the form
<pre>
&lt;scheduler>http:host.domain.edu/cgi/scheduler&lt;scheduler>
&lt;scheduler>http:host2.domain.edu/cgi/scheduler&lt;scheduler>
</pre>
that give the URLs of the project's scheduling servers.
These tags can be embedded within HTML comments.
The BOINC core client reads and parses the master page
to find scheduler servers.
If it is unable to connect to any scheduler server for a project,
it rereads the master page.
</ul>
This mechanism lets a project change the locations of its scheduling servers,
or add new servers.

25
doc/tools_security.html Normal file
View File

@ -0,0 +1,25 @@
<h3>Security tools</h3>
<p>
The program <b>lib/crypt_prog</b> can be used for several purposes:
<br>
<br>
<b>-genkey n private_keyfile public_keyfile</b>
<br>
Create a key pair with n bits (always use 1024).
Write the keys in encoded ASCII form to the indicated files.
<br>
<b>-sign file private_keyfile</b>
<br>
Create a digital signature for the given file.
Write it in encoded ASCII to stdout.
<br>
<b>-verify file signature_file public_keyfile</b>
<br>
Verify a signature for the given file.
<br>
<b>-test_crypt private_keyfile public_keyfile</b>
<br>
Perform an internal test, checking that encryption
followed by decryption works.

View File

@ -1,3 +1,7 @@
<h3>Test BOINC Project</h3>
<scheduler>http://localhost/boinc-cgi/cgi</scheduler>
<br><a href=login.php>Log in or create account</a>
<br><a href=home.php>User page</a>
<br><a href=download.php>Download core client</a>

View File

@ -11,6 +11,10 @@ CC = @CC@ $(CFLAGS) -I ../RSAEuro/source
PROGS = md5_test shmem_test synch_test crypt_prog
OBJS = \
countries.o \
parse.o
MD5_OBJS = \
md5.o \
md5_file.o
@ -25,7 +29,7 @@ CRYPT_LIBS = \
COUNTRY_OBJS = \
countries.o
all: $(PROGS) $(MD5_OBJS) $(CRYPT_OBJS)
all: $(PROGS) $(OBJS) $(MD5_OBJS) $(CRYPT_OBJS)
.C.o:
$(CC) -c -o $*.o $<

View File

@ -1,14 +1,10 @@
#include <stdio.h>
#include <malloc.h>
#include "rsaeuro.h"
extern "C" {
#include "rsa.h"
}
#include "md5_file.h"
#include "crypt.h"
// print some data in hex notation.
// write some data in hex notation.
// NOTE: since length may not be known to the reader,
// we follow the data with a non-hex character '.'
//
@ -23,6 +19,22 @@ int print_hex_data(FILE* f, DATA_BLOCK& x) {
fprintf(f, ".\n");
}
// same, but write to buffer
//
int sprint_hex_data(char* p, DATA_BLOCK& x) {
int i;
char buf[16];
strcpy(p, "");
for (i=0; i<x.len; i++) {
sprintf(buf, "%02x", x.data[i]);
strcat(p, buf);
if (i%32==31) strcat(p, "\n");
}
if (x.len%32 != 0) strcat(p, "\n");
strcat(p, ".\n");
}
// scan data in hex notation.
// stop when you reach a non-parsed character.
// NOTE: buffer must be big enough.
@ -38,6 +50,21 @@ int scan_hex_data(FILE* f, DATA_BLOCK& x) {
return 0;
}
// same, but read from buffer
//
int sscan_hex_data(char* p, DATA_BLOCK& x) {
int n;
x.len = 0;
while (1) {
n = sscanf(p, "%2x", x.data+x.len);
if (n <= 0) break;
x.len++;
p += 2;
if (*p == '\n') p++;
}
return 0;
}
// print a key in ASCII form
//
int print_key_hex(FILE* f, KEY* key, int size) {
@ -52,12 +79,13 @@ int print_key_hex(FILE* f, KEY* key, int size) {
}
int scan_key_hex(FILE* f, KEY* key, int size) {
int len, i;
int len, i, n;
fscanf(f, "%d", &key->bits);
len = size - sizeof(key->bits);
for (i=0; i<len; i++) {
fscanf(f, "%2x", key->data+i);
fscanf(f, "%2x", &n);
key->data[i] = n;
}
fscanf(f, ".");
return 0;
@ -72,14 +100,15 @@ int encrypt_private(
R_RSA_PRIVATE_KEY& key, DATA_BLOCK& in, DATA_BLOCK& out,
int& nbytes_encrypted
) {
int retval, n;
int retval, n, modulus_len;
modulus_len = (key.bits+7)/8;
n = in.len;
if (n >= key.bits-11) {
n = key.bits-11;
if (n >= modulus_len-11) {
n = modulus_len-11;
}
retval = RSAPrivateEncrypt(out.data, &out.len, in.data, n, &key);
if (retval) return retval;
nbytes_encrypted = n;
if (retval ) return retval;
nbytes_encrypted = retval;
return 0;
}
@ -88,7 +117,7 @@ int decrypt_public(R_RSA_PUBLIC_KEY& key, DATA_BLOCK& in, DATA_BLOCK& out) {
}
int sign_file(char* path, R_RSA_PRIVATE_KEY& key, DATA_BLOCK& signature) {
char md5_buf[64];
char md5_buf[MD5_LEN];
double file_length;
DATA_BLOCK in_block;
int retval, n;
@ -102,10 +131,26 @@ int sign_file(char* path, R_RSA_PRIVATE_KEY& key, DATA_BLOCK& signature) {
return 0;
}
int sign_block(DATA_BLOCK& data_block, R_RSA_PRIVATE_KEY& key, DATA_BLOCK& signature) {
char md5_buf[MD5_LEN];
int retval, n;
DATA_BLOCK in_block;
md5_block(data_block.data, data_block.len, md5_buf);
in_block.data = (unsigned char*)md5_buf;
in_block.len = strlen(md5_buf);
retval = encrypt_private(key, in_block, signature, n);
if (retval) {
printf("sign_block: encrypt_private returned %d\n", retval);
return retval;
}
return 0;
}
int verify_file(
char* path, R_RSA_PUBLIC_KEY& key, DATA_BLOCK& signature, bool& answer
) {
char md5_buf[64], clear_buf[256];
char md5_buf[MD5_LEN], clear_buf[256];
double file_length;
int n, retval;
DATA_BLOCK clear_signature;
@ -120,3 +165,27 @@ int verify_file(
answer = !strncmp(md5_buf, clear_buf, n);
return 0;
}
// verify, where both text and signature are char strings
//
int verify_string(
char* text, char* signature_text, R_RSA_PUBLIC_KEY& key, bool& answer
) {
char md5_buf[MD5_LEN];
unsigned char signature_buf[SIGNATURE_SIZE];
char clear_buf[MD5_LEN];
int retval, n;
DATA_BLOCK signature, clear_signature;
retval = md5_block((unsigned char*)text, strlen(text), md5_buf);
if (retval) return retval;
n = strlen(md5_buf);
signature.data = signature_buf;
sscan_hex_data(signature_text, signature);
clear_signature.data = (unsigned char*)clear_buf;
clear_signature.len = 256;
retval = decrypt_public(key, signature, clear_signature);
if (retval) return retval;
answer = !strncmp(md5_buf, clear_buf, n);
return 0;
}

View File

@ -1,5 +1,12 @@
#ifndef _CRYPT_
#define _CRYPT_
// some interface functions for RSAEuro
#include "rsaeuro.h"
extern "C" {
#include "rsa.h"
}
struct KEY {
unsigned short int bits;
unsigned char data[1];
@ -12,8 +19,14 @@ struct DATA_BLOCK {
#define MIN_OUT_BUFFER_SIZE MAX_RSA_MODULUS_LEN+1
// the size of a signature (encrypted MD5)
//
#define SIGNATURE_SIZE MIN_OUT_BUFFER_SIZE
int print_hex_data(FILE* f, DATA_BLOCK&);
int sprint_hex_data(char* p, DATA_BLOCK&);
int scan_hex_data(FILE* f, DATA_BLOCK&);
int sscan_hex_data(char* p, DATA_BLOCK&);
int print_key_hex(FILE*, KEY* key, int len);
int scan_key_hex(FILE*, KEY* key, int len);
int encrypt_private(
@ -21,4 +34,8 @@ int encrypt_private(
);
int decrypt_public(R_RSA_PUBLIC_KEY& key, DATA_BLOCK& in, DATA_BLOCK& out);
int sign_file(char* path, R_RSA_PRIVATE_KEY&, DATA_BLOCK& signature);
int sign_block(DATA_BLOCK& data, R_RSA_PRIVATE_KEY&, DATA_BLOCK& signature);
int verify_file(char* path, R_RSA_PUBLIC_KEY&, DATA_BLOCK& signature, bool&);
int verify_string(char* text, char* signature, R_RSA_PUBLIC_KEY&, bool&);
#endif

View File

@ -1,14 +1,14 @@
// utility program for encryption.
//
// -genkey n private_keyfile public_keyfile
// create a key pair with n bits (512 <= n <= 2048)
// create a key pair with n bits (512 <= n <= 1024)
// write it in hex notation
// -sign file private_keyfile
// create a signature for a given file
// write it in hex notation
// -verify file signature_file public_keyfile
// verify a signature
// -crypt_test private_keyfile public_keyfile
// -test_crypt private_keyfile public_keyfile
// test encrypt/decrypt
#include <stdio.h>
@ -50,6 +50,7 @@ main(int argc, char** argv) {
);
if (retval) die("R_GeneratePEMKeys\n");
printf("creating keys in %s and %s\n", argv[3], argv[4]);
fpriv = fopen(argv[3], "w");
if (!fpriv) die("fopen");
fpub = fopen(argv[4], "w");

View File

@ -32,3 +32,18 @@ int md5_file(char* path, char* output, double& nbytes) {
fclose(f);
return 0;
}
int md5_block(unsigned char* data, int nbytes, char* output) {
unsigned char binout[16];
int i;
md5_state_t state;
md5_init(&state);
md5_append(&state, data, nbytes);
md5_finish(&state, binout);
for (i=0; i<16; i++) {
sprintf(output+2*i, "%02x", binout[i]);
}
output[32] = 0;
return 0;
}

View File

@ -1 +1,5 @@
// length of buffer to hold an MD5 hash
#define MD5_LEN 64
extern int md5_file(char* path, char* output, double& nbytes);
extern int md5_block(unsigned char* data, int nbytes, char* output);

View File

@ -87,18 +87,25 @@ void copy_stream(FILE* in, FILE* out) {
}
}
void strcatdup(char*& p, char* buf) {
p = (char*)realloc(p, strlen(p) + strlen(buf)+1);
if (!p) {
fprintf(stderr, "strcatdup: realloc failed\n");
exit(1);
}
strcat(p, buf);
}
int dup_element_contents(FILE* in, char* end_tag, char** pp) {
char buf[256];
char* p = (char*)malloc(1);
*p = 0;
char* p = strdup("");
while (fgets(buf, 256, in)) {
if (strstr(buf, end_tag)) {
*pp = p;
return 0;
}
p = (char*)realloc(p, strlen(p) + strlen(buf)+1);
strcat(p, buf);
strcatdup(p, buf);
}
fprintf(stderr, "dup_element_contents(): no end tag\n");
return 1;

View File

@ -26,4 +26,5 @@ extern bool parse_str(char*, char*, char*);
extern void parse_attr(char* buf, char* attrname, char* out);
extern bool match_tag(char*, char*);
extern void copy_stream(FILE* in, FILE* out);
extern void strcatdup(char*& p, char* buf);
extern int dup_element_contents(FILE* in, char* end_tag, char** pp);

View File

@ -5,12 +5,18 @@ VPATH = @srcdir@
all: cgi
CFLAGS = -g -Wall @DEFS@ -I@top_srcdir@/db -I@top_srcdir@/lib -I@top_srcdir@/tools -I/usr/local/mysql/include
CFLAGS = -g -Wall @DEFS@ \
-I@top_srcdir@/db \
-I@top_srcdir@/lib \
-I@top_srcdir@/RSAEuro/source \
-I@top_srcdir@/tools \
-I/usr/local/mysql/include
CC = g++ $(CFLAGS)
CLIBS = @LIBS@
PROGS = cgi feeder show_shmem fcgi
PROGS = cgi feeder show_shmem file_upload_handler fcgi
all: $(PROGS)
@ -22,8 +28,7 @@ CGI_OBJS = \
../db/db_mysql.o \
../db/mysql_util.o \
../lib/shmem.o \
../lib/parse.o \
../tools/process_result_template.o
../lib/parse.o
FEEDER_OBJS = \
feeder.o \
@ -39,6 +44,14 @@ SHOW_SHMEM_OBJS = \
../db/mysql_util.o \
../lib/shmem.o
FILE_UPLOAD_OBJS = \
file_upload_handler.o \
../lib/crypt.o \
../lib/parse.o \
../lib/md5.o \
../lib/md5_file.o \
../RSAEuro/source/rsaeuro.a
FCGI_OBJS = \
handle_request.fcgi.o \
main.fcgi.o \
@ -76,6 +89,9 @@ feeder: $(FEEDER_OBJS)
show_shmem: $(SHOW_SHMEM_OBJS)
$(CC) $(SHOW_SHMEM_OBJS) $(MYSQL_LIBS) $(CLIBS) -o show_shmem
file_upload_handler: $(FILE_UPLOAD_OBJS)
$(CC) $(FILE_UPLOAD_OBJS) $(CLIBS) -o file_upload_handler
fcgi: $(FCGI_OBJS)
$(CC) $(FCGI_OBJS) $(MYSQL_LIBS) $(CLIBS) $(FCGI_LIBS) \
-o fcgi

View File

@ -1,11 +1,192 @@
// The input to this program looks like this:
//
// <result>
// <file_info>...</file_info>
// <file_info>
// ...
// <signature>
// ...
// </signature>
// </result>
// <filename>blah</filename>
// <signature>
// ...
// </signature>
// </file_info>
// <nbytes>x</nbytes>
// <offset>x</offset>
// <data>
// ... (data)
//
// The return looks like
//
// <status>0</status>
// or
// <status>2</status>
// <error>bad file size</error>
#include <stdio.h>
#include <string.h>
#include "parse.h"
#include "crypt.h"
#define BOINC_UPLOAD_DIR "/home/david/html/upload"
#define BOINC_PUBLIC_KEY_PATH "/home/david/boinc_keys/upload_public"
#define MAX_FILES 32
struct FILE_INFO {
char name[256];
double max_nbytes;
char* signature;
char* signed_xml;
int parse(FILE*);
};
int FILE_INFO::parse(FILE* in) {
char buf[256];
int retval;
memset(this, 0, sizeof(FILE_INFO));
signed_xml = strdup("");
while (fgets(buf, 256, in)) {
if (match_tag(buf, "</file_info>")) return 0;
else if (match_tag(buf, "<signature>")) {
retval = dup_element_contents(in, "</signature>", &signature);
if (retval) return retval;
continue;
}
strcatdup(signed_xml, buf);
if (parse_str(buf, "<name>", name)) continue;
if (parse_double(buf, "<max_nbytes>", max_nbytes)) continue;
//fprintf(stderr, "FILE_INFO::parse: unrecognized: %s \n", buf);
}
return 1;
}
int print_status(int status, char* message) {
printf("Content-type: text/plain\n\n<status>%d</status>\n", status);
if (message) printf("<error>%s</error>\n", message);
#if 0
fprintf(stderr, "Content-type: text/plain\n\n<status>%d</status>\n", status);
if (message) fprintf(stderr, "<error>%s</error>\n", message);
#endif
return 0;
}
#define BLOCK_SIZE 16382
// read from socket, write to file
//
int read_file(FILE* in, char* path, double offset, double nbytes) {
unsigned char buf[BLOCK_SIZE];
FILE* out;
int retval, n, m;
double bytes_left;
out = fopen(path, "wb");
if (!out) {
print_status(-1, "can't open file");
return -1;
}
// TODO: use a 64-bit variant
retval = fseek(out, (long)offset, SEEK_SET);
if (retval) {
fclose(out);
print_status(-1, "can't fseek file");
return retval;
}
bytes_left = nbytes - offset;
while (1) {
m = BLOCK_SIZE;
if (m > bytes_left) m = (int)bytes_left;
n = fread(buf, 1, m, in);
if (n <= 0) {
print_status(-1, "can't fread socket");
return -1;
}
m = fwrite(buf, 1, n, out);
if (m != n) {
print_status(-1, "can't fwrite file");
return -1;
}
bytes_left -= n;
if (bytes_left == 0) break;
}
fclose(out);
return 0;
}
int handle_request(FILE* in, R_RSA_PUBLIC_KEY& key) {
char buf[256];
double nbytes=0, offset=0;
char path[256];
FILE_INFO file_info;
int retval;
bool is_valid;
while (fgets(buf, 256, in)) {
if (match_tag(buf, "<file_info>")) {
retval = file_info.parse(in);
if (retval) return retval;
retval = verify_string(
file_info.signed_xml, file_info.signature, key, is_valid
);
if (retval || !is_valid) {
print_status(-1, "invalid signature");
return -1;
}
continue;
}
else if (parse_double(buf, "<offset>", offset)) continue;
else if (parse_double(buf, "<nbytes>", nbytes)) continue;
else if (match_tag(buf, "<data>")) {
if (nbytes == 0) {
print_status(-1, "nbytes missing");
return -1;
}
// enforce limits in signed XML
if (nbytes > file_info.max_nbytes) {
sprintf(buf,
"nbytes too large: %f > %d",
nbytes, file_info.max_nbytes
);
print_status(-1, buf);
return -1;
}
sprintf(path, "%s/%s", BOINC_UPLOAD_DIR, file_info.name);
retval = read_file(in, path, offset, nbytes);
break;
}
}
return 0;
}
int get_key(R_RSA_PUBLIC_KEY& key) {
FILE* f;
int retval;
f = fopen(BOINC_PUBLIC_KEY_PATH, "r");
if (!f) return -1;
retval = scan_key_hex(f, (KEY*)&key, sizeof(key));
fclose(f);
if (retval) return retval;
return 0;
}
int main() {
int retval;
R_RSA_PUBLIC_KEY key;
retval = get_key(key);
if (retval) {
fprintf(stderr, "can't read key file\n");
print_status(-1, "can't read key file");
exit(0);
}
retval = handle_request(stdin, key);
if (retval) {
fprintf(stderr, "handle_request: %d\n", retval);
} else {
print_status(0, 0);
}
return 0;
}

View File

@ -57,10 +57,7 @@ int SCHEDULER_REQUEST::parse(FILE* fin) {
else if (match_tag(buf, "<preferences>")) {
while (fgets(buf, 256, fin)) {
if (strstr(buf, "</preferences>")) break;
prefs_xml = (char*)realloc(
prefs_xml, strlen(prefs_xml) + strlen(buf)+1
);
strcat(prefs_xml, buf);
strcatdup(prefs_xml, buf);
}
}
else if (match_tag(buf, "<host_info>")) {

View File

@ -2,7 +2,8 @@
<name><OUTFILE_0/></name>
<generated_locally/>
<upload_when_present/>
<url><UPLOAD_URL/><OUTFILE_0/></url>
<url><UPLOAD_URL/></url>
<max_nbytes>100000</max_nbytes>
</file_info>
<result>
<name><RESULT_NAME/></name>

View File

@ -2,7 +2,8 @@
<name><OUTFILE_0/></name>
<generated_locally/>
<upload_when_present/>
<url><UPLOAD_URL/><OUTFILE_0/></url>
<url><UPLOAD_URL/></url>
<max_nbytes>100000</max_nbytes>
</file_info>
<result>
<name><RESULT_NAME/></name>

View File

@ -23,6 +23,7 @@ $BOINC_UPLOAD_DIR = null;
$BOINC_PLATFORM = null;
$BOINC_EMAIL = null;
$BOINC_URL_BASE = null;
$BOINC_KEY_DIR = null;
function check_env_vars() {
global $BOINC_DOWNLOAD_DIR;
@ -30,6 +31,7 @@ function check_env_vars() {
global $BOINC_PLATFORM;
global $BOINC_EMAIL;
global $BOINC_URL_BASE;
global $BOINC_KEY_DIR;
$bad = false;
$BOINC_DOWNLOAD_DIR = getenv("BOINC_DOWNLOAD_DIR");
@ -57,8 +59,14 @@ function check_env_vars() {
echo "Must define BOINC_URL_BASE\n";
$bad = true;
}
$BOINC_KEY_DIR = getenv("BOINC_KEY_DIR");
if ($BOINC_KEY_DIR == null) {
echo "Must define BOINC_KEY_DIR\n";
$bad = true;
}
if ($bad) exit();
}
function clear_data_dirs() {
global $BOINC_DOWNLOAD_DIR;
global $BOINC_UPLOAD_DIR;
@ -73,10 +81,16 @@ function clear_data_dirs() {
echo "Must define BOINC_UPLOAD_DIR\n";
$bad = true;
}
$BOINC_KEY_DIR = getenv("BOINC_KEY_DIR");
if ($BOINC_KEY_DIR == null) {
echo "Must define BOINC_KEY_DIR\n";
$bad = true;
}
if ($bad) exit();
PassThru("rm -f $BOINC_DOWNLOAD_DIR/*");
PassThru("rm -f $BOINC_UPLOAD_DIR/*");
PassThru("rm -f $BOINC_KEY_DIR/*");
}
function init_client_dirs($prefs_file) {
@ -143,7 +157,13 @@ function add_app($name) {
}
function create_work($x) {
PassThru("../tools/create_work $x");
global $BOINC_KEY_DIR;
PassThru("../tools/create_work -keyfile $BOINC_KEY_DIR/upload_private $x");
}
function create_keys() {
global $BOINC_KEY_DIR;
PassThru("../lib/crypt_prog -genkey 1024 $BOINC_KEY_DIR/upload_private $BOINC_KEY_DIR/upload_public");
}
function run_client($args) {

3
test/master.html Normal file
View File

@ -0,0 +1,3 @@
<h3>Test BOINC Project</h3>
<scheduler>http://localhost/boinc-cgi/cgi</scheduler>

View File

@ -2,7 +2,8 @@
<name><OUTFILE_0/></name>
<generated_locally/>
<upload_when_present/>
<url><UPLOAD_URL/><OUTFILE_0/></url>
<url><UPLOAD_URL/></url>
<max_nbytes>100000</max_nbytes>
</file_info>
<result>
<name><RESULT_NAME/></name>

View File

@ -9,6 +9,7 @@
check_env_vars();
clear_db();
clear_data_dirs();
create_keys();
init_client_dirs("prefs2.xml");
add_platform();
add_core_client();

View File

@ -9,6 +9,7 @@
check_env_vars();
clear_db();
clear_data_dirs();
create_keys();
init_client_dirs("prefs1.xml");
copy_to_download_dir("input");
add_platform(null);

View File

@ -8,6 +8,7 @@
check_env_vars();
clear_db();
clear_data_dirs();
create_keys();
init_client_dirs("prefs1.xml");
copy_to_download_dir("input");
add_platform();

View File

@ -7,6 +7,7 @@
clear_db();
clear_data_dirs();
create_keys();
init_client_dirs("prefs1.xml");
copy_to_download_dir("small_input");
add_platform();

View File

@ -7,6 +7,7 @@
clear_db();
clear_data_dirs();
create_keys();
init_client_dirs("prefs1.xml");
copy_to_download_dir("small_input");
add_platform();

View File

@ -7,6 +7,7 @@
clear_db();
clear_data_dirs();
create_keys();
init_client_dirs("prefs1.xml");
copy_to_download_dir("small_input");
add_platform();

View File

@ -8,6 +8,7 @@
check_env_vars();
clear_db();
clear_data_dirs();
create_keys();
init_client_dirs("prefs1.xml");
copy_to_download_dir("small_input");
add_platform();

View File

@ -8,6 +8,7 @@
check_env_vars();
clear_db();
clear_data_dirs();
create_keys();
init_client_dirs("prefs2.xml");
copy_to_download_dir("small_input");
add_platform();

View File

@ -8,6 +8,7 @@
check_env_vars();
clear_db();
clear_data_dirs();
create_keys();
init_client_dirs("prefs1.xml");
copy_to_download_dir("input");
add_platform();

View File

@ -9,6 +9,7 @@
check_env_vars();
clear_db();
clear_data_dirs();
create_keys();
init_client_dirs("prefs1.xml");
copy_to_download_dir("input");
add_platform(null);

View File

@ -9,6 +9,7 @@
check_env_vars();
clear_db();
clear_data_dirs();
create_keys();
init_client_dirs("prefs1.xml");
copy_to_download_dir("small_input");
add_platform(null);

View File

@ -2,7 +2,8 @@
<name><OUTFILE_0/></name>
<generated_locally/>
<upload_when_present/>
<url><UPLOAD_URL/><OUTFILE_0/></url>
<max_nbytes>100000</max_nbytes>
<url><UPLOAD_URL/></url>
</file_info>
<result>
<name><RESULT_NAME/></name>

View File

@ -2,7 +2,8 @@
<name><OUTFILE_0/></name>
<generated_locally/>
<upload_when_present/>
<url><UPLOAD_URL/><OUTFILE_0/></url>
<url><UPLOAD_URL/></url>
<max_nbytes>100000</max_nbytes>
</file_info>
<result>
<name><RESULT_NAME/></name>

View File

@ -10,11 +10,16 @@ MYSQL_INC = /usr/local/mysql/include
CFLAGS = -g -Wall @DEFS@ \
-DHOSTTYPE=\"@host@\" \
-DVERSION=$(VERSION) \
-I @top_srcdir@/lib -I @top_srcdir@/db -I $(MYSQL_INC)
-I @top_srcdir@/lib \
-I @top_srcdir@/RSAEuro/source \
-I @top_srcdir@/db \
-I $(MYSQL_INC)
CC = @CC@ $(CFLAGS)
CLIBS = @LIBS@
CLIBS = @LIBS@ \
../lib/crypt.o \
../RSAEuro/source/rsaeuro.a
OBJS = \
backend_lib.o \
@ -34,6 +39,7 @@ LIBS = \
process_result_template.o \
../lib/md5_file.o \
../lib/md5.o \
../lib/parse.o \
../db/db_mysql.o \
../db/mysql_util.o

View File

@ -167,7 +167,7 @@ void add_user() {
strcpy(user.country, "United States");
strcpy(user.postal_code, "94703");
if (prefs_file) {
retval = read_file(prefs_file, user.prefs);
retval = read_filename(prefs_file, user.prefs);
if (retval) {
printf("read_file: %s", prefs_file);
return;

View File

@ -22,6 +22,7 @@
#include <time.h>
#include "db.h"
#include "crypt.h"
#include "md5_file.h"
#include "backend_lib.h"
@ -33,18 +34,25 @@
#define OUTFILE_MACRO "<OUTFILE_"
#define UPLOAD_URL_MACRO "<UPLOAD_URL/>"
#define DOWNLOAD_URL_MACRO "<DOWNLOAD_URL/>"
#define UPLOAD_URL "http://localhost/upload/"
#define UPLOAD_URL "http://localhost/boinc-cgi/file_upload_handler"
#define DOWNLOAD_URL "http://localhost/download/"
int read_file(char* path, char* buf) {
FILE* f = fopen(path, "r");
if (!f) return -1;
int read_file(FILE* f, char* buf) {
int n = fread(buf, 1, MAX_BLOB_SIZE, f);
buf[n] = 0;
fclose(f);
return 0;
}
int read_filename(char* path, char* buf) {
int retval;
FILE* f = fopen(path, "r");
if (!f) return -1;
retval = read_file(f, buf);
fclose(f);
return retval;
}
// replace INFILE_x with filename from array,
// MD5_x with checksum of file,
// WU_NAME with WU name
@ -114,10 +122,13 @@ static int process_wu_template(
return 0;
}
int create_result(WORKUNIT& wu, char* result_template, int i) {
int create_result(
WORKUNIT& wu, char* result_template_filename, int i, R_RSA_PRIVATE_KEY& key
) {
RESULT r;
char base_outfile_name[256];
int retval;
FILE* result_template_file, *tempfile;
memset(&r, 0, sizeof(r));
r.create_time = time(0);
@ -125,23 +136,35 @@ int create_result(WORKUNIT& wu, char* result_template, int i) {
r.state = RESULT_STATE_UNSENT;
sprintf(r.name, "%s_%d", wu.name, i);
sprintf(base_outfile_name, "%s_", r.name);
strcpy(r.xml_doc_in, result_template);
result_template_file = fopen(result_template_filename, "r");
tempfile = tmpfile();
retval = process_result_template(
r.xml_doc_in, base_outfile_name, wu.name, r.name
result_template_file,
tempfile,
key,
base_outfile_name, wu.name, r.name
);
rewind(tempfile);
read_file(tempfile, r.xml_doc_in);
fclose(tempfile);
retval = db_result_new(r);
if (retval) {
fprintf(stderr, "db_result_new: %d\n", retval);
}
return retval;
}
int create_work(
WORKUNIT& wu,
char* wu_template,
char* result_template,
char* result_template_file,
int nresults,
char* infile_dir,
char** infiles,
int ninfiles
int ninfiles,
R_RSA_PRIVATE_KEY& key
) {
int i, retval;
@ -160,7 +183,7 @@ int create_work(
if (!wu.dynamic_results) {
for (i=0; i<nresults; i++) {
create_result(wu, result_template, i);
create_result(wu, result_template_file, i, key);
}
}
return 0;

View File

@ -17,11 +17,16 @@
// Contributor(s):
//
#include "crypt.h"
extern int process_result_template(
char* tmplate, char* base_filename, char* wu_name, char* result_name
FILE* in, FILE* out,
R_RSA_PRIVATE_KEY& key,
char* base_filename, char* wu_name, char* result_name
);
extern int read_file(char* path, char* buf);
extern int read_file(FILE*, char* buf);
extern int read_filename(char* path, char* buf);
extern int create_work(
WORKUNIT& wu,
@ -30,5 +35,6 @@ extern int create_work(
int nresults,
char* infile_dir,
char** infiles,
int ninfiles
int ninfiles,
R_RSA_PRIVATE_KEY&
);

View File

@ -28,6 +28,7 @@
// -wu_template filename
// -result_template filename
// -nresults n
// -keyfile path
// infile1 infile2 ...
//
// Create a workunit and results.
@ -43,6 +44,7 @@
#include <time.h>
#include "db.h"
#include "crypt.h"
#include "backend_lib.h"
int main(int argc, char** argv) {
@ -52,8 +54,10 @@ int main(int argc, char** argv) {
char wu_template[MAX_BLOB_SIZE];
char result_template[MAX_BLOB_SIZE];
char wu_template_file[256], result_template_file[256];
char keyfile[256];
char** infiles;
int i, ninfiles, nresults;
R_RSA_PRIVATE_KEY key;
char* boinc_download_dir = getenv("BOINC_DOWNLOAD_DIR");
srand(time(NULL));
@ -68,6 +72,7 @@ int main(int argc, char** argv) {
strcpy(wu_template_file, "");
strcpy(result_template_file, "");
strcpy(app.name, "");
strcpy(keyfile, "");
nresults = 1;
i = 1;
ninfiles = 0;
@ -101,7 +106,10 @@ int main(int argc, char** argv) {
wu.rsc_memory = atof(argv[i+1]);
} else if (!strcmp(argv[i], "-rsc_disk")) {
i++;
wu.rsc_disk = atof(argv[i+1]);
wu.rsc_disk = atof(argv[i]);
} else if (!strcmp(argv[i], "-keyfile")) {
i++;
strcpy(keyfile, argv[i]);
} else if (!strcmp(argv[i], "-wu_name_rand")) {
i++;
sprintf(wu.name, "%s_%d", argv[i], rand());
@ -124,25 +132,51 @@ int main(int argc, char** argv) {
exit(1);
}
retval = read_file(wu_template_file, wu_template);
if (retval) {fprintf(stderr, "can't open WU template\n"); exit(1); }
retval = read_file(result_template_file, result_template);
if (retval) {fprintf(stderr, "can't open result template\n"); exit(1); }
retval = read_filename(wu_template_file, wu_template);
if (retval) {
fprintf(stderr, "can't open WU template\n");
exit(1);
}
if (wu.dynamic_results) strcpy(app.result_xml_template, result_template);
retval = db_app_update(app);
if (retval) printf("db_app_update: %d\n", retval);
#if 0
retval = read_file(result_template_file, result_template);
if (retval) {
fprintf(stderr, "can't open result template\n");
exit(1);
}
#endif
if (wu.dynamic_results) {
strcpy(app.result_xml_template, result_template);
retval = db_app_update(app);
if (retval) printf("db_app_update: %d\n", retval);
}
wu.appid = app.id;
FILE* fkey = fopen(keyfile, "r");
if (!fkey) {
printf("create_work: can't open key file (%s)\n", keyfile);
exit(1);
}
retval = scan_key_hex(fkey, (KEY*)&key, sizeof(key));
fclose(fkey);
if (retval) {
printf("can't parse key\n");
exit(1);
}
retval = create_work(
wu,
wu_template,
result_template,
result_template_file,
nresults,
boinc_download_dir,
infiles,
ninfiles
ninfiles,
key
);
if (retval) printf("create_work: %d\n", retval);
if (retval) fprintf(stderr, "create_work: %d\n", retval);
db_close();
}

View File

@ -17,74 +17,115 @@
// Contributor(s):
//
// macro-substitute a result template file:
// - replace OUTFILE_x with base_filename_x,
// - WU_NAME with WU name
// - RESULT_NAME with result name
// - At the end of every <file_info> element, add a signature
// of its contents up to that point.
#include <string.h>
#include <stdlib.h>
#include "db.h"
#include "parse.h"
#include "crypt.h"
#define WU_NAME_MACRO "<WU_NAME/>"
#define RESULT_NAME_MACRO "<RESULT_NAME/>"
#define OUTFILE_MACRO "<OUTFILE_"
#define UPLOAD_URL_MACRO "<UPLOAD_URL/>"
#define DOWNLOAD_URL_MACRO "<DOWNLOAD_URL/>"
#define UPLOAD_URL "http://localhost/upload/"
#define UPLOAD_URL "http://localhost/boinc-cgi/file_upload_handler"
#define DOWNLOAD_URL "http://localhost/download/"
// replace OUTFILE_x with base_filename_x,
// WU_NAME with WU name
// RESULT_NAME with result name
//
int process_result_template(
char* out, char* base_filename, char* wu_name, char* result_name
FILE* in, FILE* out,
R_RSA_PRIVATE_KEY& key,
char* base_filename, char* wu_name, char* result_name
) {
char* p,*q;
char buf[MAX_BLOB_SIZE];
char* p,*q, *signed_xml=strdup("");
char buf[256], temp[256];
unsigned char signature_buf[SIGNATURE_SIZE];
DATA_BLOCK block, signature;
char num;
int i;
bool found;
while (1) {
found = false;
p = strstr(out, OUTFILE_MACRO);
if (p) {
found = true;
i = atoi(p+strlen(OUTFILE_MACRO));
q = p+strlen(OUTFILE_MACRO);
num = q[0];
strcpy(buf, p+strlen(OUTFILE_MACRO)+1+2);
strcpy(p, base_filename);
strncat(p, &num, 1);
strcat(p, buf);
while (fgets(buf, 256, in)) {
// when we reach the end of a <file_info> element,
// generate a signature for the contents thus far
//
if (match_tag(buf, "<file_info>")) {
free(signed_xml);
signed_xml = strdup("");
fputs(buf, out);
continue;
}
p = strstr(out, UPLOAD_URL_MACRO);
if (p) {
found = true;
strcpy(buf, p+strlen(UPLOAD_URL_MACRO));
strcpy(p, UPLOAD_URL);
strcat(p, buf);
if (match_tag(buf, "</file_info>")) {
block.data = (unsigned char*)signed_xml;
block.len = strlen(signed_xml);
signature.data = signature_buf;
signature.len = SIGNATURE_SIZE;
sign_block(block, key, signature);
fprintf(out, "<signature>\n");
print_hex_data(out, signature);
printf("signing [\n%s]\n", signed_xml);
printf("signature: [\n");
print_hex_data(stdout, signature);
printf("]\n");
fprintf(out, "</signature>\n");
fprintf(out, "</file_info>\n");
continue;
}
p = strstr(out, DOWNLOAD_URL_MACRO);
if (p) {
found = true;
strcpy(buf, p+strlen(DOWNLOAD_URL_MACRO));
strcpy(p, DOWNLOAD_URL);
strcat(p, buf);
while (1) {
found = false;
p = strstr(buf, OUTFILE_MACRO);
if (p) {
found = true;
i = atoi(p+strlen(OUTFILE_MACRO));
q = p+strlen(OUTFILE_MACRO);
num = q[0];
strcpy(temp, p+strlen(OUTFILE_MACRO)+1+2);
strcpy(p, base_filename);
strncat(p, &num, 1);
strcat(p, temp);
}
p = strstr(buf, UPLOAD_URL_MACRO);
if (p) {
found = true;
strcpy(temp, p+strlen(UPLOAD_URL_MACRO));
strcpy(p, UPLOAD_URL);
strcat(p, temp);
}
p = strstr(buf, DOWNLOAD_URL_MACRO);
if (p) {
found = true;
strcpy(temp, p+strlen(DOWNLOAD_URL_MACRO));
strcpy(p, DOWNLOAD_URL);
strcat(p, temp);
}
p = strstr(buf, WU_NAME_MACRO);
if (p) {
found = true;
strcpy(temp, p+strlen(WU_NAME_MACRO));
strcpy(p, wu_name);
strcat(p, temp);
}
p = strstr(buf, RESULT_NAME_MACRO);
if (p) {
found = true;
strcpy(temp, p+strlen(RESULT_NAME_MACRO));
strcpy(p, result_name);
strcat(p, temp);
}
if (!found) break;
}
p = strstr(out, WU_NAME_MACRO);
if (p) {
found = true;
strcpy(buf, p+strlen(WU_NAME_MACRO));
strcpy(p, wu_name);
strcat(p, buf);
}
p = strstr(out, RESULT_NAME_MACRO);
if (p) {
found = true;
strcpy(buf, p+strlen(RESULT_NAME_MACRO));
strcpy(p, result_name);
strcat(p, buf);
}
if (!found) break;
strcatdup(signed_xml, buf);
fputs(buf, out);
}
return 0;
}