/* Copyright 2010 Nathan Phillip Brink This file is a part of DistRen. DistRen is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. DistRen is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with DistRen. If not, see . */ #ifndef DISTREN_PROTOCOL_H #define DISTREN_PROTOCOL_H #include #include #include /** * Server types: * * It is assumed that any client has at least the functionality of a * normal client (i.e., accepts all of the REQUIRED requests). These * bitmask values add to the types of requests one may make to a * server. After first connecting to a server or receiving a * connection, a client shall send a DISTREN_REQUEST_VERSION packet * describing its version. */ #define DISTREN_SERVERTYPE_SUBMIT (0x1) #define DISTREN_SERVERTYPE_DISTRIBUTE (0x2) #define DISTREN_SERVERTYPE_RENDER (0x4) #define DISTREN_SERVERTYPE_CLIENT (0x8) /** * This file defines the constants and structs that distren clients * and server use to converse. */ /** * List of request types and metadata. */ enum distren_request_type { /** * identifies the version of software being used by the sender and * tells if it is a client or server. Sends PACKAGE_STRING as well * as informing the other server what type of server this is. * * DATA: struct distren_request followed by PACKAGE_STRING. The length of PACKAGE_STRING must be no longer than 32 bytes. * * REQUIRED: ALL */ DISTREN_REQUEST_VERSION = 1, /** * DATA: up to 32 bytes of a PING cookie * * REQUIRED: ALL */ DISTREN_REQUEST_PING = 2, /** * DATA: up to 32 bytes copied from a received PING cookie * * REQUIRED: ALL */ DISTREN_REQUEST_PONG = 3, /** * The data is the a reason describing why the one end is * disconnecting. The other end should respectfully close() * the socket or the sender will timeout shortly. * * DATA: A string describing the reason for the connection's * termination. * * REQUIRED: ALL */ DISTREN_REQUEST_DISCONNECT = 4, /** * DATA: struct distren_request_submit * * REQUIRED: DISTREN_SERVERTYPE_SUBMIT */ DISTREN_REQUEST_SUBMIT = 5, /** * Inform the other party about a job. * * DATA: struct distren_request_jobinfo * * REQUIRED: ALL */ DISTREN_REQUEST_JOBINFO = 6, /** * Request a DISTREN_REQUEST_JOBINFO * * DATA: struct distren_request_jobinfo_get * * REQUIRED: DISTREN_SERVERTYPE_SUBMIT, DISTREN_SERVERTYPE_DISTRIBUTE */ DISTREN_REQUEST_JOBINFO_GET = 7, /** * Command the other party to render a frame * * DATA: struct distren_request_frame_render * * REQUIRED: DISTREN_SERVERTYPE_RENDER */ DISTREN_REQUEST_FRAME_RENDER = 8, /** * Inform the receiver of the sender's state, such as frames being * rendered or jobs that need to be completed. * * DATA: struct distren_request_status * * REQUIRED: DISTREN_SERVERTYPE_RENDER, DISTREN_SERVERTYPE_DISTRIBUTE, DISTREN_SERVERTYPE_CLIENT */ DISTREN_REQUEST_STATUS = 9, /** * Request that the receiver send a DISTREN_REQUEST_FRAME_STATUS * packet. No data because the sending party might not beforehand * know what frames/jobs the receiving server is managing. * * DATA: (none) * * REQUIRED: DISTREN_SERVERTYPE_RENDER */ DISTREN_REQUEST_STATUS_GET = 10, /** * Declare that a client is preparing to post a file using a * particular post-id and with a user-friendly filename. * * Yes, this is evil. Yes, this tries to replicate * well-established protocols like FTP and HTTP (which is * sometimes itself mistreated as FTP). Yes, this will be * buggy. Why is it needed? For my personal coding experience and * because I don't know off-hand of any libraries that allow a * file upload to be interleaved over an existing connection with * its own protocol. Over TCP? We aren't doing real-time streaming * or anything :-p. * * Possible future expansions: ``transparent'' zlib compression. * * DATA: struct distren_request_file_post_start * * REQUIRED: DISTREN_SERVERTYPE_SUBMIT, DISTREN_SERVERTYPE_DISTRIBUTE */ DISTREN_REQUEST_FILE_POST_START = 11, /** * Allow a client to upload a job tarball over a remoteio line. A * client that wants to do this must take care not to overbuffer * his sendqueue so as to be able to respond to PING packets in * time. A server receiving such a message will want to write the * file as directly to disk as possible to avoid bogging the * server down in swap. This packet may only be sent if * DISTREN_REQUEST_FILE_POST_START has previously been sent. * * It is potential that if a server is DISTREN_SERVERTYPE_SUBMIT * but not also DISTREN_SERVERTYPE_DISTRIBUTE, that this request * would be relayed by the DISTREN_SERVERTYPE_SUBMIT server to a * DISTREN_SERVERTYPE_DISTRIBUTE server so that other clients can * obtain the file from the distribution server. In this case, the * file's URL is already known. * * Of course, having this sort of functionality at all is where * the nasty security issues start coming into play :-D. * * DATA: struct distren_request_file_post followed by a maximum of 131072 bytes (128kB). * * REQUIRED: DISTREN_SERVERTYPE_SUBMIT, DISTREN_SERVERTYPE_DISTRIBUTE */ DISTREN_REQUEST_FILE_POST = 12, /** * Marks a post-id's file as having completely uploaded. Provides * verification information so that the file's integrity may be * verified. * * DATA: struct distren_request_file_post_finish * * REQUIRED: DISTREN_SERVERTYPE_SUBMIT, DISTREN_SERVERTYPE_DISTRIBUTE */ DISTREN_REQUEST_FILE_POST_FINISH = 13, /** * Request information about obtaining a file (such as a * cURL-compatible URL) based on a distren file URL. * * DATA: struct distren_request_file_find * * REQUIRED: DISTREN_SERVERTYPE_DISTRIBUTE */ DISTREN_REQUEST_FILE_FIND = 14, /** * Provide information about obtaining a file (such as a URL). * * DATA: struct distren_request_file * * REQUIRED: DISTREN_SERVERTYPE_DISTRIBUTE */ DISTREN_REQUEST_FILE = 15, }; struct distren_request { uint32_t magic; /* the length of the data associated with the packet excluding the header */ uint32_t len; /** treat type as an enum distren_request_type using casting */ uint32_t /* enum distren_request_type */ type; }; #define DISTREN_REQUEST_VERSION_PACKAGE_STRING_LEN (32) /** * A DISTREN_REQUEST_VERSION is started with a bitmask specification * of the DISTREN_SERVERTYPE_* values. */ struct distren_request_version { uint32_t servertype; /* + 1 is for terminating NULL */ char package_string[DISTREN_REQUEST_VERSION_PACKAGE_STRING_LEN + 1]; }; #define DISTREN_REQUEST_FILE_POST_NAME_LEN (64) struct distren_request_file_post_start { /** * Uniquely identify this file upload (per connection). */ uint32_t post_id; /** * The user-friendly filename */ char filename[DISTREN_REQUEST_FILE_POST_NAME_LEN]; }; #define DISTREN_REQUEST_FILE_POST_DATA_LEN (1024*128) struct distren_request_file_post { /** * Uniquely identify this file upload (per connection). */ uint32_t post_id; }; struct distren_request_file_post_finish { /** * Uniquely identify this file upload (per connection). */ uint32_t post_id; /** * Marks this upload as failed/cancelled. */ uint32_t cancel; /** * An SHA1sum of the entire file. Only set if last_packet is * nonzero. Should be otherwise ignored. */ unsigned char sha[EVP_MAX_MD_SIZE]; }; /** * initializes and allocates request */ int distren_request_new(struct distren_request **req, uint32_t len, enum distren_request_type type); struct remoteio; /** Takes a struct distren_request and its associated data, allocates a new block of data to hold the whole packet, and packets the req header and data together. @param rem A remoteio handle to ship this packet off to @param req Something you initialized with distren_request_new(). You are responsible for distren_request_free()ing this yourself. @param data A chunk of data the size of req->len. You are responsible for free()ing this yourself. @return 0 on success and 1 on failure. */ int distren_request_send(struct remoteio *rem, struct distren_request *req, void *data); /** * initializes and allocates request based on raw input data * which includes the headers of the request. * * @return 0 on success, 1 on failure */ int distren_request_new_fromdata(struct distren_request **req, void *data, size_t len); /** * Parses requests in a way suitable to be called from a * remoteio_read_handle_func_t. * * If this function returns non-zero, you should handle the request * and then call this function again (after shortening len and * incrementing buf, of course). * * @param expectlen A pointer to a state variable that, if not NULL, can speed up this function. If you want to use this, you must set the variable to 0 before the first call and preserve the variable. * @param buf the input buffer. * @param len the length of buf. * @param req a pointer to where we should set NULL if we don't have a full request or where we store the address of a newly allocated request. You should call distren_request_free() on this. * @param req_data a pointer to where the request data pointer should be stored. This will just be set to a pointer in buf, so don't free() this. * @param err this will be set to 0 if there is no error and to 1 if there is an error and the connection should be closed. You must check this. * @return number of bytes that we have used from buf and that should be marked used. */ size_t distren_request_handle(size_t *expectlen, void *buf, size_t len, struct distren_request **req, void **req_data, short *err); /** * frees request */ int distren_request_free(struct distren_request *req); /** * An implementation of remoteio_read_handle_func_t for use with remoteio. * * To use this handler, first initialize a struct distren_request_remoteio_data. * (to be continued... or not...? ;-) ) */ /* size_t distren_request_remoteio_handle(struct remoteio *rem, void *generic_data, void *buf, size_t len, void *data); */ #endif