void POST_a_lot(void) { sleep(1); onion_block *tosend = onion_block_new(); onion_block_add_str(tosend, "POST /configuration HTTP/1.1\nHost: example.com\nContent-Type: x-application/garbage\nContent-Length: 1000000\n\n"); { int i; onion_block *bl = onion_block_new(); for (i = 0; i < 1000; i++) { onion_block_add_char(bl, rand() & 255); } for (i = 0; i < 1000; i++) { onion_block_add_block(tosend, bl); } onion_block_free(bl); } onion_block *bl = connect_and_send("127.0.0.1", "8080", tosend, 1024 * 64); onion_block_free(tosend); ONION_DEBUG("%p", strstr(onion_block_data(bl), "\n1000000\n")); FAIL_IF_NOT(strstr(onion_block_data(bl), "\n1000000\n") != NULL); onion_block_free(bl); }
void t04_server_overflow(){ INIT_LOCAL(); onion *server=onion_new(0); onion_listen_point *lp=onion_buffer_listen_point_new(); onion_add_listen_point(server,NULL,NULL,lp); onion_set_root_handler(server, onion_handler_static("Succedded", 200)); onion_block *long_req=onion_block_new(); onion_block_add_str(long_req,"GET / HTTP/1.1\n"); int i; for(i=0;i<1000;i++){ onion_block_add_str(long_req,"Header-1: This is header1 Header-2: This is header 2 "); } onion_request *req=onion_request_new(lp); onion_request_write(req, onion_block_data(long_req),onion_block_size(long_req)-1); // send it all, but the final 0. const char *buffer=onion_buffer_listen_point_get_buffer_data(req); FAIL_IF_NOT_EQUAL_STR(buffer,""); onion_request_write(req, "\n\n",2); // finish this request. no \n\n before to check possible bugs. buffer=onion_buffer_listen_point_get_buffer_data(req); FAIL_IF_EQUAL_STR(buffer,""); FAIL_IF_NOT_STRSTR(buffer, "HTTP/1.1 200 OK\r\n"); FAIL_IF_NOT_STRSTR(buffer, "\r\nContent-Length: 9\r\n"); FAIL_IF_NOT_STRSTR(buffer, "libonion"); FAIL_IF_NOT_STRSTR(buffer, "\r\n\r\nSuccedded"); onion_block_free(long_req); onion_request_free(req); onion_free(server); END_LOCAL(); }
void t02_several_add_methods(){ INIT_TEST(); onion_block *block=onion_block_new(); FAIL_IF_EQUAL(block, NULL); int i; for (i=0;i<1024;i++){ onion_block_add_char(block, (char)i); } onion_block_clear(block); onion_block_add_str(block, "first "); for (i=0;i<1024;i++) onion_block_add_str(block, "test "); FAIL_IF_NOT_STRSTR(onion_block_data(block), "test"); for (i=0;i<1024;i++) onion_block_add_data(block, "world", 4); FAIL_IF_STRSTR(onion_block_data(block), "world"); FAIL_IF_NOT_STRSTR(onion_block_data(block), "worl"); int s=onion_block_size(block); onion_block_add_block(block, block); FAIL_IF_NOT_EQUAL(onion_block_size(block), s+s); onion_block_free(block); END_TEST(); }
void POST_json(void) { sleep(1); onion_block *tosend = onion_block_new(); onion_block_add_str(tosend, "POST /configuration HTTP/1.1\nHost: example.com\nContent-Type: application/json\nContent-Length: 30\n\n" "{\n \"a\": \"10\",\n \"b\": \"20\"\n}"); onion_block *bl = connect_and_send("127.0.0.1", "8080", tosend, 1024 * 1024); FAIL_IF_NOT(strstr(onion_block_data(bl), "Done")); onion_block_free(bl); onion_block_free(tosend); }
/** * @short Prepares the response for propfinds * * @param realpath Shared folder * @param urlpath URL of the requested propfind * @param depth Depth of query, 0 or 1. * @param props Properties of the query * * @returns An onion_block with the XML data. */ onion_block *onion_webdav_write_propfind(const char *basepath, const char *realpath, const char *urlpath, int depth, int props){ onion_block *data=onion_block_new(); xmlTextWriterPtr writer; xmlBufferPtr buf; buf = xmlBufferCreate(); if (buf == NULL) { ONION_ERROR("testXmlwriterMemory: Error creating the xml buffer"); return data; } writer = xmlNewTextWriterMemory(buf, 0); if (writer == NULL) { ONION_ERROR("testXmlwriterMemory: Error creating the xml writer"); return data; } int error; xmlTextWriterStartDocument(writer, NULL, "utf-8", NULL); xmlTextWriterStartElement(writer, BAD_CAST "D:multistatus"); xmlTextWriterWriteAttribute(writer, BAD_CAST "xmlns:D" ,BAD_CAST "DAV:"); error=onion_webdav_write_props(writer, basepath, realpath, urlpath, NULL, props); if (depth>0){ ONION_DEBUG("Get also all files"); DIR *dir=opendir(realpath); if (!dir){ ONION_ERROR("Error opening dir %s to check files on it", realpath); } else{ struct dirent *de; while ( (de=readdir(dir)) ){ if (de->d_name[0]!='.') onion_webdav_write_props(writer, basepath, realpath, urlpath, de->d_name, props); } closedir(dir); } } xmlTextWriterEndElement(writer); xmlTextWriterEndElement(writer); xmlTextWriterEndDocument(writer); xmlFreeTextWriter(writer); onion_block_add_str(data, (const char*)buf->content); xmlBufferFree(buf); if (error){ onion_block_free(data); return NULL; } return data; }
/** * @short Prepares the PUT * * It saves the data to a temporal file, which name is stored at data. */ static onion_connection_status prepare_PUT(onion_request *req){ onion_token *token=req->parser_data; const char *content_size=onion_dict_get(req->headers, "Content-Length"); if (!content_size){ ONION_ERROR("I need the Content-Length header to get data"); return OCS_INTERNAL_ERROR; } size_t cl=atol(content_size); if (cl>req->connection.listen_point->server->max_file_size){ ONION_ERROR("Trying to PUT a file bigger than allowed size"); return OCS_INTERNAL_ERROR; } req->data=onion_block_new(); char filename[]="/tmp/onion-XXXXXX"; int fd=mkstemp(filename); if (fd<0) ONION_ERROR("Could not create temporary file at %s.", filename); onion_block_add_str(req->data, filename); ONION_DEBUG0("Creating PUT file %s (%d bytes long)", filename, token->extra_size); if (!req->FILES){ req->FILES=onion_dict_new(); } { const char *filename=onion_block_data(req->data); onion_dict_add(req->FILES,"filename", filename, 0); } if (cl==0){ ONION_DEBUG0("Created 0 length file"); close(fd); return OCS_REQUEST_READY; } int *pfd=onion_low_scalar_malloc(sizeof(fd)); *pfd=fd; assert(token->extra==NULL); token->extra=(char*)pfd; token->extra_size=cl; token->pos=0; req->parser=parse_PUT; return OCS_NEED_MORE_DATA; }
void t01_create_and_free(){ INIT_TEST(); onion_block *block=onion_block_new(); FAIL_IF_EQUAL(block, NULL); int i; for (i=0;i<16*1024;i++){ onion_block_add_char(block, (char)i); } onion_block_free(block); END_TEST(); }
/** * @short Creates a new function on top of the stack. * * It can receive a fmt argument that sets the id. If none, its assigned automatically. */ function_data *function_new(parser_status *st, const char *fmt, ...){ function_data *d=malloc(sizeof(function_data)); d->flags=0; d->code=onion_block_new(); if (st){ st->current_code=d->code; list_add(st->function_stack, d); list_add(st->functions, d); //ONION_DEBUG("push function stack, length is %d", list_count(st->function_stack)); } d->signature=NULL; char tmp[512]; // Here at begining was 128, but somehow it was corrupting stack. Stack cheap here, so no risks. if (!fmt){ d->is_static=1; snprintf(tmp, sizeof(tmp), "otemplate_f_%04X",st->function_count++); } else{ d->is_static=0; va_list va; va_start(va, fmt); vsnprintf(tmp,sizeof(tmp),fmt,va); va_end(va); char *p=tmp; while (*p){ if (*p=='.') *p='_'; if (*p=='-') *p='_'; p++; } if (isdigit(tmp[0])){ memmove(tmp+1,tmp,strlen(tmp)+1); tmp[0]='_'; } } ONION_DEBUG("New function %s", tmp); d->id=strdup(tmp); return d; }
/** * @short Converts a dict to a json string * @memberof onion_dict_t * * Given a dictionary and a buffer (with size), it writes a json dictionary to it. * * @returns an onion_block with the json data, or NULL on error */ onion_block *onion_dict_to_json(onion_dict *dict){ onion_block *block=onion_block_new(); onion_block_add_char(block, '{'); if (dict && dict->root) onion_dict_node_preorder(dict->root, (void*)onion_dict_json_preorder, block); int s=onion_block_size(block); if (s==0){ // Error. onion_block_free(block); return NULL; } if (s!=1) // To remove a final ", " onion_block_rewind(block, 2); onion_block_add_char(block, '}'); return block; }
/** * @short Prepares the CONTENT LENGTH */ static onion_connection_status prepare_CONTENT_LENGTH(onion_request *req){ onion_token *token=req->parser_data; const char *content_size=onion_dict_get(req->headers, "Content-Length"); if (!content_size){ ONION_ERROR("I need the Content-Length header to get data"); return OCS_INTERNAL_ERROR; } size_t cl=atol(content_size); if (cl>req->connection.listen_point->server->max_post_size){ ONION_ERROR("Trying to set more data at server than allowed %d", req->connection.listen_point->server->max_post_size); return OCS_INTERNAL_ERROR; } req->data=onion_block_new(); token->extra=NULL; token->extra_size=cl; token->pos=0; req->parser=parse_CONTENT_LENGTH; return OCS_NEED_MORE_DATA; }
onion_dict *onion_dict_from_json_(const char **_data){ const char *data=*_data; ONION_DEBUG("Parse %s", *_data); while (is_json_space(*data)) ++data; if (*data!='{') return NULL; ++data; while (is_json_space(*data)) ++data; ; onion_dict *ret=onion_dict_new(); onion_block *key=onion_block_new(); onion_block *value=onion_block_new(); while (*data!='}'){ // Get Key ssize_t read_bytes=onion_json_unquote_add(key, data); if (read_bytes<0) goto error; data+=read_bytes; while (is_json_space(*data)) ++data; /// Get : if (*data!=':'){ // Includes \0 ONION_DEBUG("Expected : got %c", *data); goto error; } ++data; while (is_json_space(*data)) ++data; /// Get Value if (*data=='{'){ // Includes \0 *_data=data; onion_dict *sub=onion_dict_from_json_(_data); if (!sub){ goto error; } onion_dict_add(ret, onion_block_data(key), sub, OD_DUP_KEY|OD_DICT|OD_FREE_VALUE); data=*_data; } else if (is_json_digit(*data)){ while(is_json_digit(*data)){ onion_block_add_char(value, *data); ++data; } onion_dict_add(ret, onion_block_data(key), onion_block_data(value), OD_DUP_ALL); } else if (*data=='"'){ // parse string ssize_t read_bytes=onion_json_unquote_add(value, data); if (read_bytes<0) goto error; data+=read_bytes; onion_dict_add(ret, onion_block_data(key), onion_block_data(value), OD_DUP_ALL); onion_block_clear(value); } else { // Includes \0 ONION_DEBUG("Expected \" got %c", *data); goto error; } onion_block_clear(key); while (is_json_space(*data)) ++data; if (*data=='}'){ ++data; *_data=data; onion_block_free(key); onion_block_free(value); return ret; } if (*data!=','){ ONION_DEBUG("Expected , got %c", *data); goto error; } ++data; while (is_json_space(*data)) ++data; } ++data; *_data=data; onion_block_free(key); onion_block_free(value); return ret; error: onion_block_free(key); onion_block_free(value); onion_dict_free(ret); return NULL; }
onion_block *connect_and_send(const char *ip, const char *port, const onion_block * msg, size_t maxsize) { int fd; { struct addrinfo hints; struct addrinfo *server; memset(&hints, 0, sizeof(struct addrinfo)); hints.ai_socktype = SOCK_STREAM; hints.ai_family = AF_UNSPEC; hints.ai_flags = AI_PASSIVE | AI_NUMERICSERV; if (getaddrinfo(ip, port, &hints, &server) < 0) { ONION_ERROR("Error getting server info"); return NULL; } fd = socket(server->ai_family, server->ai_socktype | SOCK_CLOEXEC, server->ai_protocol); if (connect(fd, server->ai_addr, server->ai_addrlen) == -1) { close(fd); fd = -1; ONION_ERROR("Error connecting to server %s:%s", ip, port); return NULL; } freeaddrinfo(server); } size_t left = onion_block_size(msg); const char *data = onion_block_data(msg); while (left > 0) { ONION_DEBUG("."); int towrite = (left > maxsize) ? maxsize : left; int ret = write(fd, data, towrite); FAIL_IF(ret <= 0); if (ret <= 0) { ONION_ERROR("Error sending data."); return NULL; } left -= ret; data += ret; } onion_block *bl = onion_block_new(); char tmp[256]; int r = 0; int total = 0; do { r = read(fd, tmp, sizeof(tmp)); ONION_DEBUG("+ %d", r); if (r > 0) { total += r; onion_block_add_data(bl, tmp, r); } } while (r > 0); ONION_DEBUG("Total %d", total); FAIL_IF(total == 0); close(fd); return bl; }
/** * @short Compiles the infilename to outfilename. */ int work(const char *infilename, const char *outfilename, onion_assets_file *assets){ tag_init(); parser_status status; memset(&status, 0, sizeof(status)); status.mode=TEXT; status.functions=list_new((void*)function_free); status.function_stack=list_new(NULL); status.status=0; status.line=1; status.rawblock=onion_block_new(); status.infilename=infilename; char tmp2[256]; strncpy(tmp2, infilename, sizeof(tmp2)-1); const char *tname=basename(tmp2); ONION_DEBUG("Create init function on top, tname %s",tname); status.blocks_init=function_new(&status, "%s_blocks_init", tname); status.blocks_init->signature="onion_dict *context"; if (strcmp(infilename, "-")==0) status.in=stdin; else status.in=fopen(infilename,"rt"); if (!status.in){ ONION_ERROR("Could not open in file %s", infilename); goto work_end; } ONION_DEBUG("Create main function on top, tname %s",tname); function_new(&status, tname); function_add_code(&status, " int has_context=(context!=NULL);\n" " if (!has_context)\n" " context=onion_dict_new();\n" " \n" " %s(context);\n", status.blocks_init->id); parse_template(&status); ((function_data*)status.function_stack->tail->data)->flags=0; function_add_code(&status, " if (!has_context)\n" " onion_dict_free(context);\n" ); if (status.status){ ONION_ERROR("Parsing error"); goto work_end; } if (strcmp(outfilename, "-")==0) status.out=stdout; else status.out=fopen(outfilename,"wt"); if (!status.out){ ONION_ERROR("Could not open out file %s", infilename); goto work_end; } fprintf(status.out, "/** Autogenerated by otemplate v. 0.2.0 */\n" "\n" "#include <libintl.h>\n" "#include <string.h>\n\n" "#include <onion/onion.h>\n" "#include <onion/dict.h>\n" "\n" "typedef struct dict_res_t{\n" " onion_dict *dict;\n" " onion_response *res;\n" "}dict_res;\n" "\n" "\n"); functions_write_declarations_assets(&status, assets); functions_write_declarations(&status); functions_write_main_code(&status); if (use_orig_line_numbers) fprintf(status.out, "#line 1 \"%s\"\n", infilename); functions_write_code(&status); work_end: if (status.in) fclose(status.in); if (status.out) fclose(status.out); list_free(status.functions); list_free(status.function_stack); //list_free(status.blocks); onion_block_free(status.rawblock); tag_free(); return status.status; }
/** * @short Solves a variable into code. * * It uses the type to check it its a literal string, a dcit string or a dict. */ void variable_solve(parser_status *st, const char *data, const char *tmpname, vartype_e type){ if (type==LITERAL){ char *s=onion_c_quote_new(data); function_add_code(st, " %s=%s;\n", tmpname, s); free(s); return; } if (! (type==STRING || type==DICT) ){ ONION_ERROR("Invalid type for variable solve"); exit(1); } list *parts=list_new(onion_block_free); onion_block *lastblock; list_add(parts, lastblock=onion_block_new()); int i=0; int l=strlen(data); const char *d=data; for (i=0;i<l;i++){ if (d[i]=='.') list_add(parts, lastblock=onion_block_new()); else if (d[i]==' ') continue; else onion_block_add_char(lastblock, d[i]); } if (list_count(parts)==1){ char *s=onion_c_quote_new(onion_block_data(lastblock)); if (type==STRING) function_add_code(st, " %s=onion_dict_get(context, %s);\n", tmpname, s); else if (type==DICT) function_add_code(st, " %s=onion_dict_get_dict(context, %s);\n", tmpname, s); free(s); } else{ if (type==STRING) function_add_code(st," %s=onion_dict_rget(context", tmpname); else if (type==DICT) function_add_code(st," %s=onion_dict_rget_dict(context", tmpname); else{ ONION_ERROR("Invalid type for variable solve"); exit(1); } list_item *it=parts->head; while (it){ lastblock=it->data; char *s=onion_c_quote_new(onion_block_data(lastblock)); function_add_code(st,", %s", s); free(s); it=it->next; } function_add_code(st,", NULL);\n"); } list_free(parts); }
onion_dict *onion_dict_from_json_(const char **_data){ const char *data=*_data; ONION_DEBUG("Parse %s", *_data); while (isspace(SAFETY_CAST(*data))) ++data; if (*data!='{') return NULL; ++data; while (isspace(SAFETY_CAST(*data))) ++data; ; onion_dict *ret=onion_dict_new(); onion_block *key=onion_block_new(); onion_block *value=onion_block_new(); while (*data!='}'){ // Get Key if (*data!='"'){ // Includes \0 ONION_DEBUG("Expected \" got %c", *data); goto error; } ++data; while (*data!='"'){ if (!*data){ // \0 ONION_DEBUG("Expected \" got eof"); goto error; } onion_block_add_char(key, *data); ++data; } ++data; while (isspace(SAFETY_CAST(*data))) ++data; /// Get : if (*data!=':'){ // Includes \0 ONION_DEBUG("Expected : got %c", *data); goto error; } ++data; while (isspace(SAFETY_CAST(*data))) ++data; /// Get Value if (*data=='{'){ // Includes \0 *_data=data; onion_dict *sub=onion_dict_from_json_(_data); if (!sub){ goto error; } onion_dict_add(ret, onion_block_data(key), sub, OD_DUP_KEY|OD_DICT|OD_FREE_VALUE); data=*_data; } else if (isdigit(SAFETY_CAST(*data))){ while(isdigit(SAFETY_CAST(*data))){ onion_block_add_char(value, *data); ++data; } onion_dict_add(ret, onion_block_data(key), onion_block_data(value), OD_DUP_ALL); } else if (*data=='"'){ ++data; while (*data!='"'){ if (!*data){ // \0 ONION_DEBUG("Expected \" got eof"); goto error; } onion_block_add_char(value, *data); ++data; } ++data; onion_dict_add(ret, onion_block_data(key), onion_block_data(value), OD_DUP_ALL); onion_block_clear(value); } else { // Includes \0 ONION_DEBUG("Expected \" got %c", *data); goto error; } onion_block_clear(key); while (isspace(SAFETY_CAST(*data))) ++data; if (*data=='}'){ ++data; *_data=data; onion_block_free(key); onion_block_free(value); return ret; } if (*data!=','){ ONION_DEBUG("Expected , got %c", *data); goto error; } ++data; while (isspace(SAFETY_CAST(*data))) ++data; } ++data; *_data=data; onion_block_free(key); onion_block_free(value); return ret; error: onion_block_free(key); onion_block_free(value); onion_dict_free(ret); return NULL; }