Files @ c318109520cc
Branch filter:

Location: DistRen/src/common/protocol.h

binki
User authentication and some access checking.
/*
  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 <http://www.gnu.org/licenses/>.
*/

#ifndef DISTREN_PROTOCOL_H
#define DISTREN_PROTOCOL_H

#include <openssl/evp.h>
#include <stddef.h>
#include <stdint.h>

/**
 * 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,
    /**
     * Test if the end has a live server.
     *
     * Only authenticated clients may use this request. Thus, if a
     * client wants to confirm that a DISTREN_REQUEST_PASS request was
     * successful, that client may send a DISTREN_REQUEST_PING
     * immediately after the DISTREN_REQUEST_PASS and then wait for
     * the PONG.
     *
     * DATA: up to 32 bytes of a PING cookie
     *
     * REQUIRED: ALL
     */
    DISTREN_REQUEST_PING = 2,
    /**
     * Response to DISTREN_REQUEST_PING.
     *
     * Unauthenticated clients may be penalized for responding to PING
     * requests. This is because a newly connecting client should
     * queue a DISTREN_REQUEST_VERSION and DISTREN_REQUEST_PASS
     * back-to-back before checking for and processing data from the
     * remote server.
     *
     * 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,


    /**
     * Allow a client to identify itself using simple password
     * authentication.
     *
     * As there is no distren request which affirms a
     * DISTREN_REQUEST_PASS went through, clients may send a
     * DISTREN_REQUEST_PING and wait for the DISTREN_REQUETS_PONG they
     * want to block until they're authenticated.
     *
     * DATA: struct distren_request_pass
     *
     * REQUIRED: DISTREN_SERVERTYPE_SUBMIT (for now, since
     * server2server links are only protected using password
     * authentication, all server types have to support this except
     * for the client.)
     */
    DISTREN_REQUEST_PASS = 5,

    /**
     * DATA: struct distren_request_submit
     *
     * REQUIRED: DISTREN_SERVERTYPE_SUBMIT
     */
    DISTREN_REQUEST_SUBMIT = 6,

    /**
     * Inform the other party about a job.
     *
     * DATA: struct distren_request_jobinfo
     *
     * REQUIRED: ALL
     */
    DISTREN_REQUEST_JOBINFO = 7,

    /**
     * Request a DISTREN_REQUEST_JOBINFO
     *
     * DATA: struct distren_request_jobinfo_get
     *
     * REQUIRED: DISTREN_SERVERTYPE_SUBMIT, DISTREN_SERVERTYPE_DISTRIBUTE
     */
    DISTREN_REQUEST_JOBINFO_GET = 8,

    /**
     * Command the other party to render a frame
     *
     * DATA: struct distren_request_frame_render
     *
     * REQUIRED: DISTREN_SERVERTYPE_RENDER
     */
    DISTREN_REQUEST_FRAME_RENDER = 9,
    /**
     * 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 = 10,

    /**
     * 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 = 11,

    /**
     * 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 = 12,

    /**
     * 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 = 13,

    /**
     * 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 = 14,

    /**
     * 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 = 15,

    /**
     * Provide information about obtaining a file (such as a URL).
     *
     * DATA: struct distren_request_file
     *
     * REQUIRED: DISTREN_SERVERTYPE_DISTRIBUTE
     */
    DISTREN_REQUEST_FILE = 16,
  };

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_PASS_USERNAME_LEN (16)
#define DISTREN_REQUEST_PASS_PASS_LEN (32)
struct distren_request_pass
{
  char username[DISTREN_REQUEST_PASS_USERNAME_LEN];
  char pass[DISTREN_REQUEST_PASS_PASS_LEN];
};

#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