unofficial mirror of bug-gnu-emacs@gnu.org 
 help / color / mirror / code / Atom feed
From: Michael Welsh Duggan <mwd@cert.org>
To: Alan Mackenzie <acm@muc.de>
Cc: "11749@debbugs.gnu.org" <11749@debbugs.gnu.org>,
	Kim Storm <storm@cua.dk>
Subject: bug#11749: Acknowledgement (24.1; C-mode indentation gives wrong-type-argument error.)
Date: Thu, 17 Jan 2013 11:28:01 -0500	[thread overview]
Message-ID: <tntbocnu63y.fsf@waterbuck.yellow.cert.org> (raw)
In-Reply-To: <tntfw1zu65j.fsf@waterbuck.yellow.cert.org> (Michael Welsh Duggan's message of "Thu, 17 Jan 2013 11:27:04 -0500")

[-- Attachment #1: Type: text/plain, Size: 48 bytes --]

And here's the file I was supposed to include:


[-- Attachment #2: rwtransfer.c --]
[-- Type: text/plain, Size: 50818 bytes --]

/*
** Copyright (C) 2006-2013 by Carnegie Mellon University.
**
** @OPENSOURCE_HEADER_START@
**
** Use of the SILK system and related source code is subject to the terms
** of the following licenses:
**
** GNU Public License (GPL) Rights pursuant to Version 2, June 1991
** Government Purpose License Rights (GPLR) pursuant to DFARS 252.227.7013
**
** NO WARRANTY
**
** ANY INFORMATION, MATERIALS, SERVICES, INTELLECTUAL PROPERTY OR OTHER
** PROPERTY OR RIGHTS GRANTED OR PROVIDED BY CARNEGIE MELLON UNIVERSITY
** PURSUANT TO THIS LICENSE (HEREINAFTER THE "DELIVERABLES") ARE ON AN
** "AS-IS" BASIS. CARNEGIE MELLON UNIVERSITY MAKES NO WARRANTIES OF ANY
** KIND, EITHER EXPRESS OR IMPLIED AS TO ANY MATTER INCLUDING, BUT NOT
** LIMITED TO, WARRANTY OF FITNESS FOR A PARTICULAR PURPOSE,
** MERCHANTABILITY, INFORMATIONAL CONTENT, NONINFRINGEMENT, OR ERROR-FREE
** OPERATION. CARNEGIE MELLON UNIVERSITY SHALL NOT BE LIABLE FOR INDIRECT,
** SPECIAL OR CONSEQUENTIAL DAMAGES, SUCH AS LOSS OF PROFITS OR INABILITY
** TO USE SAID INTELLECTUAL PROPERTY, UNDER THIS LICENSE, REGARDLESS OF
** WHETHER SUCH PARTY WAS AWARE OF THE POSSIBILITY OF SUCH DAMAGES.
** LICENSEE AGREES THAT IT WILL NOT MAKE ANY WARRANTY ON BEHALF OF
** CARNEGIE MELLON UNIVERSITY, EXPRESS OR IMPLIED, TO ANY PERSON
** CONCERNING THE APPLICATION OF OR THE RESULTS TO BE OBTAINED WITH THE
** DELIVERABLES UNDER THIS LICENSE.
**
** Licensee hereby agrees to defend, indemnify, and hold harmless Carnegie
** Mellon University, its trustees, officers, employees, and agents from
** all claims or demands made against them (and any related losses,
** expenses, or attorney's fees) arising out of, or relating to Licensee's
** and/or its sub licensees' negligent use or willful misuse of or
** negligent conduct or willful misconduct regarding the Software,
** facilities, or other rights or assistance granted by Carnegie Mellon
** University under this License, including, but not limited to, any
** claims of product liability, personal injury, death, damage to
** property, or violation of any laws or regulations.
**
** Carnegie Mellon University Software Engineering Institute authored
** documents are sponsored by the U.S. Department of Defense under
** Contract FA8721-05-C-0003. Carnegie Mellon University retains
** copyrights in all material produced under this contract. The U.S.
** Government retains a non-exclusive, royalty-free license to publish or
** reproduce these documents, or allow others to do so, for U.S.
** Government purposes only pursuant to the copyright license under the
** contract clause at 252.227.7013.
**
** @OPENSOURCE_HEADER_END@
*/

/*
**  rwtransfer.c
**
**    This file contains functions that are common to rwsender and
**    rwreceiver, such as options processing and establishing the
**    connection.
**
*/


#include <silk/silk.h>

RCSIDENT("$Id$");

#include <silk/utils.h>
#include <silk/sklog.h>
#include <silk/skdaemon.h>
#include "rwtransfer.h"


/* LOCAL DEFINES AND TYPEDEFS */

#if SK_ENABLE_GNUTLS
#  define UNUSED_NO_GNUTLS(x) x
#else
#  define UNUSED_NO_GNUTLS(x) UNUSED(x)
#endif

/* Illegal ident characters */
#define ILLEGAL_IDENT_CHARS " \t:/\\.,"

/* Define lowest protocol version which we handle */
#define LOW_VERSION  1

/* Version protocol we emit */
#define EMIT_VERISION 2

/* Turn on PKCS12 support */
#define PKCS12 1

/* Environment variable used to turn off keepalive.  Used for
 * debugging. */
#define RWTRANSFER_TURN_OFF_KEEPALIVE "RWTRANSFER_TURN_OFF_KEEPALIVE"

/* Maximum expected size of connection information string*/
#define RWTRANSFER_CONNECTION_TYPE_SIZE_MAX 50

typedef struct connection_msg_data_st {
    const char *name;
    int32_t     size;
} connection_msg_data_t;


/* EXPORTED VARIABLE DEFINITIONS */

int main_retval = EXIT_SUCCESS;


/* LOCAL VARIABLE DEFINITIONS */

/* Mode (client/server) */
static enum {CLIENT, SERVER, NOT_SET} mode;

/* Marks last client/server-specific option*/
static int client_sentinel;
static int server_sentinel;

/* Daemon identity */
static char *identity;

#if SK_ENABLE_GNUTLS
/* Encryption and authentication files */
static char *tls_ca_file     = NULL;
static char *tls_cert_file   = NULL;
static char *tls_key_file    = NULL;
#ifdef PKCS12
static char *tls_pkcs12_file = NULL;
#endif
#endif /* SK_ENABLE_GNUTLS */

/* Message queue */
static sk_msg_queue_t *control;

/* Temporary transfer_t item */
static transfer_t *global_temp_item;

/* Control message thread */
static pthread_t control_thread;
static int       control_thread_valid;

/* Address upon which to listen for incoming connections */
static sk_sockaddr_array_t *listen_address = NULL;
static const char *listen_address_arg = NULL;

/* Locations which can be addressed as return values */
static void *exit_standard   = &exit_standard;
static void *exit_disconnect = &exit_disconnect;
static void *exit_failure    = &exit_failure;

/* Main thread */
static pthread_t main_thread;

/* Detached thread entry/exit control (see comment in serverMain()) */
static uint16_t detached_thread_count = 0;
static pthread_mutex_t detached_thread_mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t  detached_thread_cond  = PTHREAD_COND_INITIALIZER;

typedef int (*connection_fn_t)(
    sk_msg_queue_t *,
    struct sockaddr *,
    socklen_t,
    skm_channel_t *);

typedef struct conn_info_st {
    sk_msg_queue_t *queue;
    skm_channel_t   channel;
    transfer_t     *trnsfr;
    unsigned        tls;
} conn_info_t;

typedef enum {
    /* Global options */
    OPT_MODE, OPT_IDENT
#if SK_ENABLE_GNUTLS
    , OPT_TLS_CA,
    OPT_TLS_CERT,
    OPT_TLS_KEY
#ifdef PKCS12
    , OPT_TLS_PKCS12
#endif
#endif /* SK_ENABLE_GNUTLS */
} appOptionsEnum;

typedef enum {
    /* Client options */
    OPT_SERVER_ADDR,

    /* Unused */
    OPT_CLIENT_UNUSED
} appClientOptionsEnum;

typedef enum {
    /* Server options */
    OPT_SERVER_PORT, OPT_CLIENT_IDENT,

    /* Unused */
    OPT_SERVER_UNUSED
} appServerOptionsEnum;


static struct option appOptions[] = {
    {"mode",                 REQUIRED_ARG, 0, OPT_MODE},
    {"identifier",           REQUIRED_ARG, 0, OPT_IDENT},
#if SK_ENABLE_GNUTLS
    {"tls-ca",               REQUIRED_ARG, 0, OPT_TLS_CA},
    {"tls-cert",             REQUIRED_ARG, 0, OPT_TLS_CERT},
    {"tls-key",              REQUIRED_ARG, 0, OPT_TLS_KEY},
#ifdef PKCS12
    {"tls-pkcs12",           REQUIRED_ARG, 0, OPT_TLS_PKCS12},
#endif
#endif /* SK_ENABLE_GNUTLS */
    {0,0,0,0}           /* sentinel entry */
};

static const char *appHelp[] = {
    ("Whether to run as a client or a server ('client', 'server')"),
    ("Named identifier for the daemon"),
#if SK_ENABLE_GNUTLS
    ("Full path to CA cert PEM file. Def. None. Either --tls-key\n"
     "\tand --tls-key or --tls-pkcs12 must also be specified."),
    ("Full path to encryption cert PEM file. Def. None.  Requires\n"
     "\tthat --tls-ca and --tls-key also be specified."),
    ("Full path to encryption key PEM file. Def. None.  Requires\n"
     "\tthat --tls-ca and --tls-cert also be specified."),
#ifdef PKCS12
    ("Full path to encryption cert and key PKCS#12 file.\n"
     "\tDef. None.  Requires that --tls-ca also be specified."),
#endif
#endif /* SK_ENABLE_GNUTLS */
    (char *)NULL
};

static struct option appClientOptions[] = {
    {"server-address", REQUIRED_ARG, 0, OPT_SERVER_ADDR},
    {0,0,0,0}           /* sentinel entry */
};

static const char *appClientHelp[] = {
    ("A server to which to connect.  Repeatable.\n"
     "\t<ident>:<address>:<port>"),
    (char *)NULL
};

static struct option appServerOptions[] = {
    {"server-port",  REQUIRED_ARG, 0, OPT_SERVER_PORT},
    {"client-ident", REQUIRED_ARG, 0, OPT_CLIENT_IDENT},
    {0,0,0,0}           /* sentinel entry */
};

static const char *appServerHelp[] = {
    ("[HOST:]PORT upon which to listen for incoming client\n"
     "\tconnections"),
    ("The identifier of a client allowed to connect to\n"
     "\tthis server.  Repeatable."),
    (char *)NULL
};


/*
 *  Connection message textual representation and lengths.
 *
 *  Length of -1 indicates a variable length message (use of
 *  sendString() implies variable length).
 */
static connection_msg_data_t conn_msg_data[CONN_NUMBER_OF_CONNECTION_MESSAGES]=
    {
        {"CONN_SENDER_VERSION",    sizeof(uint32_t)},
        {"CONN_RECEIVER_VERSION",  sizeof(uint32_t)},
        {"CONN_IDENT",            -1},
        {"CONN_READY",             0},
        {"CONN_DISCONNECT_RETRY", -1},
        {"CONN_DISCONNECT",       -1},
        {"CONN_NEW_FILE",         -1},
        {"CONN_NEW_FILE_READY",    0},
        {"CONN_FILE_BLOCK",       -1},
        {"CONN_FILE_COMPLETE",     0},
        {"CONN_DUPLICATE_FILE",   -1},
        {"CONN_REJECT_FILE",      -1}
    };


/* LOCAL FUNCTION PROTOTYPES */

static void *clientMain(void *); /* Thread entry point */
static void *serverMain(void *); /* Thread entry point */
static int appOptionsHandler(
    clientData  cData,
    int         opt_index,
    char       *opt_arg);
static int appClientOptionsHandler(
    clientData  cData,
    int         opt_index,
    char       *opt_arg);
static int appServerOptionsHandler(
    clientData  cData,
    int         opt_index,
    char       *opt_arg);
static void parseServerAddress(
    const char *const_addr);
static void addIdent(
    const char *ident);


/* FUNCTION DEFINITIONS */

/* checkIdent();
 *
 *    Check to see if an ident is legal.  If illegal, print an error
 *    message to the error stream, end exit.
 */
int checkIdent(
    const char     *ident)
{
    const char *invalid;
    const char *c;

    if (ident == NULL || ident[0] == '\0') {
        skAppPrintErr("Identifiers must contain at least one character");
        exit(EXIT_FAILURE);
    }
    invalid = strpbrk(ident, ILLEGAL_IDENT_CHARS);
    if (invalid != NULL) {
        skAppPrintErr(("The identifier %s contains the illegal "
                       "character %c"), ident, *invalid);
        exit(EXIT_FAILURE);
    }
    for (c = ident; *c; c++) {
        if (!isprint((int)*c)) {
            skAppPrintErr(("The identifier %s contains the illegal "
                           "nonprintable character 0x%x"), ident, (int)*c);
            exit(EXIT_FAILURE);
        }
    }

    return 0;
}


/*
 * appModeUsage();
 *
 *    Print usage information for the mode named 'mode_str', using the
 *    given 'options' and 'help'.
 */
static void appModeUsage(
    FILE           *fh,
    const char     *mode_str,
    struct option   options[],
    const char     *help[])
{
    unsigned int i;

    fprintf(fh, "\n%s switches:\n", mode_str);
    for (i = 0; options[i].name; i++) {
        if (help[i]) {
            fprintf(fh, "--%s %s. %s\n", options[i].name,
                    SK_OPTION_HAS_ARG(options[i]), help[i]);
        }
    }
}


void transferUsageLong(
    FILE           *fh,
    const char     *usage,
    struct option   options[],
    const char     *help[])
{
    unsigned int i;
    fprintf(fh, "%s %s", skAppName(), usage);

    fprintf(fh, "\nCommon switches:\n");
    skOptionsDefaultUsage(fh);
    for (i = 0; appOptions[i].name; ++i) {
        fprintf(fh, "--%s %s. %s\n", appOptions[i].name,
                SK_OPTION_HAS_ARG(appOptions[i]), appHelp[i]);
    }
    for (i = 0; options[i].name; ++i) {
        fprintf(fh, "--%s %s. %s\n", options[i].name,
                SK_OPTION_HAS_ARG(options[i]), help[i]);
    }
    appModeUsage(fh, "Client", appClientOptions, appClientHelp);
    appModeUsage(fh, "Server", appServerOptions, appServerHelp);
    fprintf(fh, "\nLogging and daemon switches:\n");
    skdaemonOptionsUsage(fh);
}


int transferSetup(
    void)
{
    /* verify that the sizes of options and help match */
    assert((sizeof(appHelp)/sizeof(char*)) ==
           (sizeof(appOptions)/sizeof(struct option)));
    assert((sizeof(appClientHelp)/sizeof(char*)) ==
           (sizeof(appClientOptions)/sizeof(struct option)));
    assert((sizeof(appServerHelp)/sizeof(char*)) ==
           (sizeof(appServerOptions)/sizeof(struct option)));

    mode                  = NOT_SET;
    client_sentinel       = OPT_CLIENT_UNUSED;
    server_sentinel       = OPT_SERVER_UNUSED;
    identity              = NULL;
    global_temp_item      = NULL;
    control_thread_valid  = 0;

    /* register the options and handler */
    if (skOptionsRegister(appOptions, &appOptionsHandler, NULL))
    {
        skAppPrintErr("Unable to transfer application options");
        return -1;
    }

    /* register the client options and handler */
    if (skOptionsRegister(appClientOptions, &appClientOptionsHandler, NULL))
    {
        skAppPrintErr("Unable to register client options");
        return -1;
    }

    /* register the server options and handler */
    if (skOptionsRegister(appServerOptions, &appServerOptionsHandler, NULL))
    {
        skAppPrintErr("Unable to register server options");
        return -1;
    }

    return 0;
}


/*
 *  status = appOptionsHandler(cData, opt_index, opt_arg);
 *
 *    This function is passed to skOptionsRegister(); it will be called
 *    by skOptionsParse() for each user-specified switch that the
 *    application has registered; it should handle the switch as
 *    required---typically by setting global variables---and return 1
 *    if the switch processing failed or 0 if it succeeded.  Returning
 *    a non-zero from from the handler causes skOptionsParse() to return
 *    a negative value.
 *
 *    The clientData in 'cData' is typically ignored; 'opt_index' is
 *    the index number that was specified as the last value for each
 *    struct option in appOptions[]; 'opt_arg' is the user's argument
 *    to the switch for options that have a REQUIRED_ARG or an
 *    OPTIONAL_ARG.
 */
static int appOptionsHandler(
    clientData  UNUSED(cData),
    int         opt_index,
    char       *opt_arg)
{

    switch ((appOptionsEnum)opt_index) {

      case OPT_MODE:
        if (0 == strcmp(opt_arg, "server")) {
            mode = SERVER;
        } else if (0 == strcmp(opt_arg, "client")) {
            mode = CLIENT;
        } else {
            skAppPrintErr("Invalid --%s '%s'",
                          appOptions[opt_index].name, opt_arg);
            return 1;
        }
        break;

      case OPT_IDENT:
        checkIdent(opt_arg);
        identity = opt_arg;
        break;

#if SK_ENABLE_GNUTLS
      case OPT_TLS_CA:
        if (optionsFileCheck(appOptions[opt_index].name, opt_arg)) {
            return 1;
        }
        tls_ca_file = opt_arg;
        break;

      case OPT_TLS_CERT:
        if (optionsFileCheck(appOptions[opt_index].name, opt_arg)) {
            return 1;
        }
        tls_cert_file = opt_arg;
        break;

      case OPT_TLS_KEY:
        if (optionsFileCheck(appOptions[opt_index].name, opt_arg)) {
            return 1;
        }
        tls_key_file = opt_arg;
        break;

#ifdef PKCS12
      case OPT_TLS_PKCS12:
        if (optionsFileCheck(appOptions[opt_index].name, opt_arg)) {
            return 1;
        }
        tls_pkcs12_file = opt_arg;
        break;
#endif
#endif /* SK_ENABLE_GNUTLS */

    }

    return 0;  /* OK */
}


int transferVerifyOptions(
    void)
{
    RBLIST *list;
    transfer_t *item;
    int error_count = 0;

    /* Check mode options */
    if (mode == NOT_SET) {
        skAppPrintErr(("Client or server mode must be chosen "
                       "via the --%s switch"),
                      appOptions[OPT_MODE].name);
        ++error_count;
    }
    if ((mode == CLIENT && (server_sentinel != OPT_SERVER_UNUSED)) ||
        (mode == SERVER && (client_sentinel != OPT_CLIENT_UNUSED)))
    {
        int badopt = (mode == CLIENT) ? server_sentinel : client_sentinel;
        struct option *opts =
            (mode == CLIENT) ? appServerOptions : appClientOptions;
        const char *mode_string = (mode == CLIENT) ? "client" : "server";
        skAppPrintErr("The --%s switch cannot be used in %s mode",
                      opts[badopt].name, mode_string);
        return -1;
    }

    if (identity == NULL) {
        skAppPrintErr("The --%s switch is required",
                      appOptions[OPT_IDENT].name);
        ++error_count;
    }

    if (rbmin(transfers) == NULL && mode != NOT_SET) {
        skAppPrintErr("Must supply at least one --%s switch",
                      (mode == CLIENT) ?
                      appClientOptions[OPT_SERVER_ADDR].name :
                      appServerOptions[OPT_CLIENT_IDENT].name);
        ++error_count;
    }

#if SK_ENABLE_GNUTLS
#ifdef PKCS12
    if (tls_ca_file || tls_cert_file || tls_key_file || tls_pkcs12_file) {
        if (!tls_ca_file) {
            skAppPrintErr("A CA cert file must be specified for "
                          "encryption: --%s", appOptions[OPT_TLS_CA].name);
            ++error_count;
        }
        if (!(tls_cert_file && tls_key_file && !tls_pkcs12_file) &&
            !(!tls_cert_file && !tls_key_file && tls_pkcs12_file))
        {
            skAppPrintErr("When using encryption, you must specify --%s and "
                          "--%s, or just --%s",
                          appOptions[OPT_TLS_CERT].name,
                          appOptions[OPT_TLS_KEY].name,
                          appOptions[OPT_TLS_PKCS12].name);
            ++error_count;
        }
    }
#else /* !PKCS12 */
    if (tls_ca_file || tls_cert_file || tls_key_file) {
        if (!tls_ca_file) {
            skAppPrintErr("A CA cert file must be specified for "
                          "encryption: --%s", appOptions[OPT_TLS_CA]);
            ++error_count;
        }
        if (!(tls_cert_file && tls_key_file))
        {
            skAppPrintErr("When using encryption, you must specify --%s and "
                          "--%s",
                          appOptions[OPT_TLS_CERT].name,
                          appOptions[OPT_TLS_KEY].name);
            ++error_count;
        }
    }
#endif /* PKCS12 */
#endif /* SK_ENABLE_GNUTLS */

    switch (mode) {
      case SERVER:
        if (listen_address == NULL) {
            skAppPrintErr("Must supply a port using --%s in server mode",
                          appServerOptions[OPT_SERVER_PORT].name);
            ++error_count;
        }
        break;

      case CLIENT:
        list = rbopenlist(transfers);
        if (list == NULL) {
            skAppPrintErr("Memory allocation failure verifying options");
            return -1;
        }
        while ((item = (transfer_t *)rbreadlist(list)) != NULL) {
            if (item->address_exists == 0) {
                skAppPrintErr("Ident %s has no address associated with it",
                              item->ident);
                return -1;
            }
        }
        rbcloselist(list);
        break;

      case NOT_SET:
        break;
    }

    if (error_count) {
        return -1;
    }

    main_thread = pthread_self();

    return 0;
}


void transferShutdown(
    void)
{
    RBLIST *iter;
    transfer_t *trnsfr;
    int rv;

    assert(shuttingdown);

    skMsgQueueShutdownAll(control);

    iter = rbopenlist(transfers);
    CHECK_ALLOC(iter);
    while ((trnsfr = (transfer_t *)rbreadlist(iter)) != NULL) {
        rv = transferUnblock(trnsfr);
        if (rv != 0) {
            CRITMSG("Unexpected failure to unblock transfer");
            _exit(EXIT_FAILURE);
        }
    }
    rbcloselist(iter);
}


void transferTeardown(
    void)
{
    /* Wait for transfer threads to end.  In server mode, all these
     * threads are detached, and as such may not be joinable.  */
    if (mode != SERVER) {
        RBLIST *iter;
        transfer_t *trnsfr;

        iter = rbopenlist(transfers);
        CHECK_ALLOC(iter);
        while ((trnsfr = (transfer_t *)rbreadlist(iter)) != NULL) {
            if (trnsfr->thread_exists) {
                DEBUGMSG("Waiting for thread %s to end...", trnsfr->ident);
                pthread_join(trnsfr->thread, NULL);
                DEBUGMSG("Thread %s has ended.", trnsfr->ident);
            }
        }
        rbcloselist(iter);
    }

    /* Wait for control thread to end */
    if (control_thread_valid) {
        DEBUGMSG("Waiting for control thread to end...");
        pthread_join(control_thread, NULL);
        DEBUGMSG("Control thread has ended.");
    }

    /* Wait for detached threads to end */
    DEBUGMSG("Waiting for detached threads to end...");
    pthread_mutex_lock(&detached_thread_mutex);
    while (detached_thread_count) {
        pthread_cond_wait(&detached_thread_cond, &detached_thread_mutex);
    }
    pthread_mutex_unlock(&detached_thread_mutex);
    DEBUGMSG("Detached threads have ended.");

    /* Destroy stuff */
    skMsgQueueDestroy(control);
    if (listen_address) {
        skSockaddrArrayDestroy(listen_address);
        listen_address = NULL;
    }
    if (global_temp_item != NULL) {
        free(global_temp_item);
    }

#if SK_ENABLE_GNUTLS
    skMsgGnuTLSTeardown();
#endif
}


/*
 *  file_exists = optionsFileCheck(opt_name, opt_arg);
 *
 *    Verify that the file in 'opt_arg' exists and that we have a full
 *    path to the file.  Verify that the length is shorter than
 *    PATH_MAX.  If so, return 0; otherwise, print an error that the
 *    option named by 'opt_name' was bad and return -1.
 */
int optionsFileCheck(
    const char *opt_name,
    const char *opt_arg)
{
    if (!opt_arg || !opt_arg[0]) {
        skAppPrintErr("Invalid %s: The argument empty", opt_name);
        return -1;
    }

    if (strlen(opt_arg)+1 >= PATH_MAX) {
        skAppPrintErr("Invalid %s: Path is too long", opt_name);
        return -1;
    }

    if (!skFileExists(opt_arg)) {
        skAppPrintErr("Invalid %s: File '%s' does not exist",
                      opt_name, opt_arg);
        return -1;
    }

    if (opt_arg[0] != '/') {
        skAppPrintErr(("Invalid %s: Must use complete path"
                       " ('%s' does not begin with slash)"),
                      opt_name, opt_arg);
        return -1;
    }

    return 0;
}


static int appClientOptionsHandler(
    clientData  UNUSED(cData),
    int         opt_index,
    char       *opt_arg)
{
    client_sentinel = opt_index;

    switch ((appClientOptionsEnum)opt_index) {
      case OPT_SERVER_ADDR:
        parseServerAddress(opt_arg);
        break;

      default:
        /* Should not get here */
        ASSERT_ABORT(0);
    }

    return 0;  /* OK */
}


static int appServerOptionsHandler(
    clientData  UNUSED(cData),
    int         opt_index,
    char       *opt_arg)
{
    int rv;

    server_sentinel = opt_index;

    switch ((appServerOptionsEnum)opt_index) {
      case OPT_CLIENT_IDENT:
        addIdent(opt_arg);
        break;

      case OPT_SERVER_PORT:
        rv = skStringParseHostPortPair(&listen_address,
                                       opt_arg, PORT_REQUIRED);
        if (rv) {
            skAppPrintErr("Invalid %s '%s': %s",
                          appOptions[opt_index].name, opt_arg,
                          skStringParseStrerror(rv));
            return 1;
        }
        listen_address_arg = opt_arg;
        break;

      default:
        /* Should not get here */
        ASSERT_ABORT(0);
    }

    return 0;  /* OK */
}



/* String compare for receiver rbtree */
static int transferCompare(
    const void *va,
    const void *vb,
    const void *UNUSED(cbdata))
{
    const transfer_t *a = (const transfer_t *)va;
    const transfer_t *b = (const transfer_t *)vb;
    return strcmp(a->ident, b->ident);
}


struct rbtree *transferIdentTreeCreate(
    void)
{
    return rbinit(transferCompare, NULL);
}


/* Create temporary transfer_t objects */
transfer_t *initTemp(
    void)
{
    /* Allocate and/or clear the temporary item */
    if (global_temp_item == NULL) {
        global_temp_item = (transfer_t *)calloc(1, sizeof(*global_temp_item));
    } else {
        memset(global_temp_item, 0, sizeof(*global_temp_item));
    }
    return global_temp_item;
}

/* If a program wishes to keep a temporary transfer object, it should
   call this. */
void clearTemp(
    void)
{
    global_temp_item = NULL;
}


/* Parse a <ident>:<address>:<port> specification */
static void parseServerAddress(
    const char     *const_addr)
{
#define FMT_PARSE_FAILURE                               \
    ("Server address parse failure parsing '%s'\n"      \
     "\tCorrect form is <ident>:<address>:<port>")
#define FMT_MEM_FAILURE                                         \
    "Memory allocation failure when parsing server address %s"

    char *addr = strdup(const_addr);
    char *colon, *next;
    transfer_t *old;
    const void *test;
    int rv;
    transfer_t *temp_item;

    temp_item = initTemp();

    if (addr == NULL || temp_item == NULL) {
        skAppPrintErr(FMT_MEM_FAILURE, const_addr);
        exit(EXIT_FAILURE);
    }

    /* First, extract the ident */
    colon = strchr(addr, ':');
    if (colon == NULL) {
        free(addr);
        skAppPrintErr(FMT_PARSE_FAILURE, const_addr);
        exit(EXIT_FAILURE);
    }
    *colon = '\0';
    checkIdent(addr);
    temp_item->ident = addr;


    /* See if it has already been used */
    old = (transfer_t *)rbfind(temp_item, transfers);
    if (old != NULL) {
        if (!old->address_exists) {
            memcpy(temp_item, old, sizeof(*temp_item));
            test = rbdelete(old, transfers);
            assert(test == old);
            temp_item->ident = addr;
            free(old->ident);
            free(old);
        } else {
            free(addr);
            skAppPrintErr("Duplicate ident in server address %s", const_addr);
            exit(EXIT_FAILURE);
        }
    }

    /* Next, extract the address */
    next = colon + 1;
    rv = skStringParseHostPortPair(&temp_item->addr, next,
                                   HOST_REQUIRED | PORT_REQUIRED);
    if (rv < 0) {
        skAppPrintErr("Could not parse address: %s",
                      skStringParseStrerror(rv));
        exit(EXIT_FAILURE);
    }

    /* Add to our server list */
    temp_item->ident = strdup(temp_item->ident);
    free(addr);
    if (temp_item->ident == NULL) {
        skAppPrintErr(FMT_MEM_FAILURE, const_addr);
        exit(EXIT_FAILURE);
    }
    test = rbsearch(temp_item, transfers);
    if (test == NULL) {
        skAppPrintErr(FMT_MEM_FAILURE, const_addr);
        exit(EXIT_FAILURE);
    }
    temp_item->address_exists = 1;

    assert(test == temp_item);
    clearTemp();

#undef FMT_PARSE_FAILURE
#undef FMT_MEM_FAILURE
}


/* Add a bare ident to the transfer list */
static void addIdent(
    const char     *ident)
{
#define FMT_MEM_FAILURE "Memory allocation failure when parsing ident %s"
    const void *test;
    transfer_t *temp_item;

    checkIdent(ident);
    temp_item = initTemp();
    if (temp_item == NULL) {
        skAppPrintErr(FMT_MEM_FAILURE, ident);
        exit(EXIT_FAILURE);
    }
    temp_item->ident = (char *)ident;
    test = rbsearch(temp_item, transfers);
    if (test == NULL) {
        skAppPrintErr(FMT_MEM_FAILURE, ident);
        exit(EXIT_FAILURE);
    }
    if (test != temp_item) {
        skAppPrintErr("Duplicate ident %s", ident);
        exit(EXIT_FAILURE);
    }
    temp_item->ident = strdup(ident);
    if (temp_item->ident == NULL) {
        skAppPrintErr(FMT_MEM_FAILURE, ident);
        exit(EXIT_FAILURE);
    }
    clearTemp();
#undef FMT_MEM_FAILURE
}


static void getConnectionInformation(
    sk_msg_queue_t *queue,
    skm_channel_t   channel,
    char           *buffer,
    size_t          buffer_size)
{
    int rv;

    rv = skMsgGetConnectionInformation(queue, channel, buffer, buffer_size);
    if (rv == -1) {
        strncpy(buffer, "<unknown>", buffer_size);
    }
    buffer[buffer_size - 1] = '\0';
}


int handleDisconnect(
    sk_msg_t       *msg,
    const char     *type)
{
    skm_type_t msgtyp = skMsgType(msg);

    if (msgtyp == CONN_DISCONNECT || msgtyp == CONN_DISCONNECT_RETRY) {
        int length = MAX_ERROR_MESSAGE;

        if (skMsgLength(msg) < length) {
            length = skMsgLength(msg);
        }

        INFOMSG("Connection %s disconnected: %.*s",
                type, length, (char *)skMsgMessage(msg));

        return (msgtyp == CONN_DISCONNECT) ? -1 : 1;
    }

    return 0;
}


/*
 *    This function is used by servers and clients.  The function
 *    verifies the connection (version, ident), and then calls the
 *    transferFiles() function defined in rwsender.c or rwreceiver.c.
 *
 *    For a server, this is a THREAD ENTRY POINT.  Entry point for the
 *    "connection" thread, started from serverMain().  This thread is
 *    detached.
 *
 *    For a client, this function is called by startClientConnection()
 *    once the client has connected to a server.
 */
static void *handleConnection(
    void           *vinfo)
{
    conn_info_t *info = (conn_info_t *)vinfo;
    transfer_t target;
    transfer_t *trnsfr = NULL;
    transfer_t *found = NULL;
    uint32_t version;
    skm_channel_t channel;
    sk_msg_queue_t *q;
    enum conn_state {Version, Ident, Ready, Running, Disconnect} state;
    int proto_err;
    int fatal_err = 0;
    const char *ident = "<unassigned>";
    void *retval = exit_failure;
    char connection_type[RWTRANSFER_CONNECTION_TYPE_SIZE_MAX];
    int transferred_file = 0;

    DEBUG_PRINT1("connection thread started");

    q = info->queue;
    channel = info->channel;
    trnsfr = info->trnsfr;
    free(info);

    /* start by sending my version and waiting for remote's version */
    state = Version;
    version = htonl(EMIT_VERISION);
    proto_err = skMsgQueueSendMessage(q, channel, local_version_check,
                                      &version, sizeof(version));

    while (!shuttingdown && !proto_err && !fatal_err && state != Running) {
        int rv;
        sk_msg_t *msg;

        rv = skMsgQueueGetMessage(q, &msg);
        if (rv == -1) {
            ASSERT_ABORT(shuttingdown);
            continue;
        }
        DEBUG_PRINT3("handleConnection() state=%d, got msg type=%d",
                     (int)state, (int)skMsgType(msg));

        rv = handleDisconnect(msg, ident);
        if (rv) {
            proto_err = 1;
            retval = transferred_file ? exit_disconnect : exit_failure;
            state = Disconnect;
        }

        switch (state) {
          case Version:
            /* expecting remote's version. if not valid, close the
             * channel.  if valid, send my ident and wait for remote's
             * ident */
            if ((proto_err = checkMsg(msg, q, remote_version_check))) {
                DEBUG_PRINT2("checkMsg(%s) FAILED",
                             conn_msg_data[remote_version_check].name);
                retval = exit_failure;
                break;
            }
            DEBUG_PRINT2("Received %s",
                         conn_msg_data[remote_version_check].name);
            version = MSG_UINT32(msg);
            if (version < LOW_VERSION) {
                sendString(q, skMsgChannel(msg), EXTERNAL,
                           CONN_DISCONNECT, LOG_WARNING,
                           ("Unsupported version %" PRIu32), version);
                proto_err = 1;
                retval = exit_failure;
                break;
            }
            if (!getenv(RWTRANSFER_TURN_OFF_KEEPALIVE)) {
                rv = skMsgSetKeepalive(q, channel, KEEPALIVE_TIMEOUT);
                assert(rv == 0);
            }
            state = Ident;
            proto_err = skMsgQueueSendMessage(q, channel, CONN_IDENT,
                                              identity, strlen(identity) + 1);
            if (proto_err != 0) {
                retval = exit_failure;
            }
            break;

          case Ident:
            /* expecting remote's ident.  if not valid, close the
             * channel.  if valid, send CONN_READY and wait for remote
             * to say it is ready */
            if ((proto_err = checkMsg(msg, q, CONN_IDENT))) {
                DEBUG_PRINT1("checkMsg(CONN_IDENT) FAILED");
                retval = exit_failure;
                break;
            }
            DEBUG_PRINT1("Received CONN_IDENT");
            target.ident = MSG_CHARP(msg);
            found = (transfer_t *)rbfind(&target, transfers);
            if (found == NULL
                || (trnsfr != NULL && trnsfr != found)
                || (trnsfr == NULL && found->thread_exists))
            {
                const char *reason;
                if (found == NULL) {
                    reason = "Unknown ident";
                } else if (trnsfr != NULL && trnsfr != found) {
                    reason = "Unexpected ident";
                } else {
                    reason = "Duplicate ident";
                }

                sendString(q, skMsgChannel(msg), EXTERNAL,
                           CONN_DISCONNECT, LOG_WARNING,
                           "%s %s", reason, target.ident);
                proto_err = 1;
                retval = exit_failure;
                break;
            }
            ident = found->ident;
            found->thread = pthread_self();
            found->thread_exists = 1;
            found->channel = channel;
            found->channel_exists = 1;
            found->remote_version = version;

            getConnectionInformation(q, channel, connection_type,
                                     sizeof(connection_type));
            INFOMSG("Connected to remote %s (%s, Protocol v%" PRIu32 ")",
                    ident, connection_type, version);
            state = Ready;
            proto_err = skMsgQueueSendMessage(q, channel, CONN_READY, NULL, 0);
            if (proto_err != 0) {
                DEBUG_PRINT1("skMsgQueueSendMessage(CONN_READY) failed");
                retval = exit_failure;
            }
            break;

          case Ready:
            /* expecting remote to say it is ready. if ready, call
             * transferFiles() */
            if ((proto_err = checkMsg(msg, q, CONN_READY))) {
                DEBUG_PRINT1("checkMsg(CONN_READY) FAILED");
                retval = exit_failure;
                break;
            }
            DEBUGMSG("Remote %s is ready for messages", ident);
            state = Running;
            rv = transferFiles(q, channel, found);
            switch (rv) {
              case -1:
                fatal_err = 1;
                break;
              case 1:
                transferred_file = 1;
                break;
              default:
                break;
            }
            break;

          case Disconnect:
            DEBUG_PRINT1("Disconnecting");
            break;

          case Running:
            ASSERT_ABORT(0);
        }

        /* Free message */
        skMsgDestroy(msg);
    }

    if (found) {
        found->channel_exists = 0;
        found->disconnect = 0;
    }

    skMsgQueueDestroy(q);

    /* If running in server mode, this was a detached thread. */
    if (trnsfr == NULL) {
        if (found) {
            found->thread_exists = 0;
        }
        pthread_mutex_lock(&detached_thread_mutex);
        detached_thread_count--;
        pthread_cond_signal(&detached_thread_cond);
        pthread_mutex_unlock(&detached_thread_mutex);
    }

    DEBUG_PRINT2("connection thread ended (status = %s)",
                 ((fatal_err)
                  ? "exit_failure [from transferFiles()]"
                  : ((exit_standard == retval)
                     ? "exit_standard"
                     : ((exit_disconnect == retval)
                        ? "exit_disconnect"
                        : ((exit_failure == retval)
                           ? "exit_failure"
                           : "UNKNOWN")))));

    if (fatal_err) {
        threadExit(EXIT_FAILURE, exit_failure);
    }

    return retval;
}


static int attemptBind(
    const sk_sockaddr_array_t  *addr,
    unsigned                   *UNUSED_NO_GNUTLS(tls),
    const char                **UNUSED_NO_GNUTLS(connection_type))
{
#if SK_ENABLE_GNUTLS
    if (tls_ca_file) {
        *tls = 1;
        *connection_type = "TCP, TLS";
        return skMsgQueueBindTLS(control, addr);
    }
#endif /* SK_ENABLE_GNUTLS */

    return skMsgQueueBindTCP(control, addr);
}


/*
 *    THREAD ENTRY POINT
 *
 *    Entry point for the "server_main" thread, started from
 *    startTransferDaemon().
 */
static void *serverMain(
    void    UNUSED(*dummy))
{
    int rv;
    unsigned tls = 0;
    const char *connection_type = "TCP";

    control_thread_valid = 1;

    DEBUG_PRINT1("server_main() thread started");

    assert(listen_address);

    rv = attemptBind(listen_address, &tls, &connection_type);
    if (rv < 0) {
        CRITMSG("Failed to bind to %s for listening", listen_address_arg);
        threadExit(EXIT_FAILURE, NULL);
    }

    INFOMSG("Bound to %s for listening (%s)",
            listen_address_arg, connection_type);

    while (!shuttingdown) {
        sk_msg_t *msg;
        skm_channel_t channel;
        pthread_t thread;
        conn_info_t *info;
        transfer_t *item;
        RBLIST *list;
        sk_new_channel_info_t *addr_info;
        char buf[PATH_MAX];
        char conn_type[RWTRANSFER_CONNECTION_TYPE_SIZE_MAX];

        rv = skMsgQueueGetMessageFromChannel(control, SKMSG_CHANNEL_CONTROL,
                                             &msg);
        if (rv == -1) {
            ASSERT_ABORT(shuttingdown);
            continue;
        }

        switch (skMsgType(msg)) {

          case SKMSG_CTL_NEW_CONNECTION:
            DEBUG_PRINT1("Received SKMSG_CTL_NEW_CONNECTION");
            channel = SKMSG_CTL_MSG_GET_CHANNEL(msg);
            addr_info = (sk_new_channel_info_t *)skMsgMessage(msg);
            getConnectionInformation(control, channel, conn_type,
                                     sizeof(conn_type));
            if (addr_info->known) {
                skSockaddrString(buf, sizeof(buf), &addr_info->addr);
            }
            INFOMSG("Received connection from %s (%s)",
                    (addr_info->known ? buf :
                     "unknown address"), conn_type);
            info = (conn_info_t *)calloc(1, sizeof(*info));
            if (info == NULL) {
                CRITMSG("Memory allocation failure");
                threadExit(EXIT_FAILURE, NULL);
            }
            info->tls = tls;
            info->trnsfr = NULL;
            rv = skMsgChannelSplit(control, channel, &info->queue);
            if (rv != 0) {
                if (shuttingdown) {
                    break;
                }
                CRITMSG("Failed to split channel");
                threadExit(EXIT_FAILURE, NULL);
            }
            info->channel = channel;

            /* In server mode we don't have one thread per ident.
             * Instead we have one thread per entity that is
             * connecting to us.  Since there is no transfer object to
             * attach the thread to, we create a detached thread
             * instead, and use the detached_thread_mutex and
             * detached_thread_count to know when the threads have
             * ended. */
            pthread_mutex_lock(&detached_thread_mutex);
            rv = skthread_create_detached("connection", &thread,
                                          handleConnection, info);
            if (rv != 0) {
                pthread_mutex_unlock(&detached_thread_mutex);
                CRITMSG("Failed to create connection thread: %s",
                        strerror(rv));
                threadExit(EXIT_FAILURE, NULL);
            }
            detached_thread_count++;
            pthread_mutex_unlock(&detached_thread_mutex);
            break;

          case SKMSG_CTL_CHANNEL_DIED:
            DEBUG_PRINT1("Received SKMSG_CTL_CHANNEL_DIED");
            channel = SKMSG_CTL_MSG_GET_CHANNEL(msg);
            list = rbopenlist(transfers);
            CHECK_ALLOC(list);
            while ((item = (transfer_t *)rbreadlist(list)) != NULL) {
                if (item->channel_exists && channel == item->channel) {
                    INFOMSG("Channel to %s died", item->ident);
                    item->disconnect = 1;
                    rv = transferUnblock(item);
                    if (rv != 0) {
                        threadExit(EXIT_FAILURE, NULL);
                    }
                    break;
                }
            }
            rbcloselist(list);

            if (!shuttingdown) {
                sendString(control, channel, INTERNAL, CONN_DISCONNECT_RETRY,
                           LOG_INFO, "Remote side of channel died");
            }
            break;

          default:
            WARNINGMSG("Received unknown control message %d", skMsgType(msg));
        }

        skMsgDestroy(msg);
    }

    DEBUG_PRINT1("server_main() thread ended");

    return NULL;
}


/*
 *    THREAD ENTRY POINT
 *
 *    Entry point for the "connection" thread, started from
 *    clientMain().
 */
static void *startClientConnection(
    void           *vitem)
{
    transfer_t *item = (transfer_t *)vitem;
    void *exit_status = exit_standard;
    int waitsecs = 0;
    const char *connection_type = "TCP";
    connection_fn_t connection_function;
    socklen_t addrlen;
    int tls = 0;
    char buf[SK_NUM2DOT_STRLEN];


    item->thread_exists = 1;

    DEBUG_PRINT1("client_connection() thread started");

    while (!shuttingdown) {
        size_t i;
        int rv;
        skm_channel_t channel;

        if (waitsecs != 0) {
            int waitcount = waitsecs;

            DEBUG_PRINT2("Failure in connection, "
                         "waiting %d seconds", waitcount);
            while (waitcount-- && !shuttingdown) {
                sleep(1);
            }
            if (shuttingdown) {
                break;
            }
        }

        connection_function = skMsgQueueConnectTCP;
#if SK_ENABLE_GNUTLS
        if (tls_ca_file) {
            tls = 1;
            connection_function = skMsgQueueConnectTLS;
            connection_type = "TCP, TLS";
        }
#endif /* SK_ENABLE_GNUTLS */

        INFOMSG("Attempting to connect to %s (%s)...",
                item->ident, connection_type);

        for (rv = -1, i = 0;
             rv != 0 && i < skSockaddrArraySize(item->addr); i++)
        {
            sk_sockaddr_t *addr = skSockaddrArrayGet(item->addr, i);
            switch (addr->sa.sa_family) {
              case AF_INET:
                addrlen = sizeof(addr->v4);
                break;
              case AF_INET6:
                addrlen = sizeof(addr->v6);
                break;
              default:
                continue;
            }
            skSockaddrString(buf, sizeof(buf), addr);
            DEBUGMSG("Address for %s is %s", item->ident, buf);
            rv = connection_function(control, &addr->sa, addrlen, &channel);
        }

        if (rv != 0) {
            INFOMSG("Attempt to connect to %s failed (%s)",
                    item->ident, connection_type);
            if (waitsecs < 60) {
                waitsecs += 5;
            }
        } else {
            conn_info_t *info;
            char conn_type[RWTRANSFER_CONNECTION_TYPE_SIZE_MAX];

            getConnectionInformation(control, channel, conn_type,
                                     sizeof(conn_type));
            DEBUGMSG("Connected (expecting ident %s) (%s)",
                     item->ident, conn_type);
            info = (conn_info_t *)calloc(1, sizeof(*info));
            if (info == NULL) {
                CRITMSG("Memory allocation failure");
                threadExit(EXIT_FAILURE, exit_failure);
            }
            info->tls = tls;
            info->trnsfr = item;
            rv = skMsgChannelSplit(control, channel, &info->queue);
            if (rv != 0) {
                if (shuttingdown) {
                    break;
                }
                CRITMSG("Failed to split channel");
                threadExit(EXIT_FAILURE, exit_failure);
            }

            info->channel = channel;
            exit_status = handleConnection(info);
            if (exit_status != exit_failure) {
                waitsecs = 0;
            } else if (waitsecs < 60) {
                waitsecs += 5;
            }
        }
    }

    DEBUG_PRINT1("client_connection() thread ended");

    return exit_status;
}


/*
 *    THREAD ENTRY POINT
 *
 *    Entry point for the "client_main" thread, started from
 *    startTransferDaemon()
 */
static void *clientMain(
    void    UNUSED(*dummy))
{
    RBLIST *list;
    transfer_t *item;
    int rv;

    control_thread_valid = 1;

    DEBUG_PRINT1("client_main() thread started");

    list = rbopenlist(transfers);
    if (list == NULL) {
        skAppPrintErr("Memory allocation failure stating client thread");
        threadExit(EXIT_FAILURE, NULL);
    }

    /* Start client threads */
    while ((item = (transfer_t *)rbreadlist(list)) != NULL) {
        rv = skthread_create("connection", &item->thread,
                             startClientConnection, item);
        if (rv != 0) {
            CRITMSG("Failed to create connection thread: %s", strerror(rv));
            threadExit(EXIT_FAILURE, NULL);
        }
    }
    rbcloselist(list);

    /* Start control loop */
    while (!shuttingdown) {
        sk_msg_t *msg;
        skm_channel_t channel;

        rv = skMsgQueueGetMessageFromChannel(control, SKMSG_CHANNEL_CONTROL,
                                             &msg);
        if (rv == -1) {
            assert(shuttingdown);
            continue;
        }

        switch (skMsgType(msg)) {

          case SKMSG_CTL_NEW_CONNECTION:
            /* This can't happen, as we aren't bound */
            ASSERT_ABORT(0);
            break;

          case SKMSG_CTL_CHANNEL_DIED:
            DEBUG_PRINT1("Received SKMSG_CTL_CHANNEL_DIED");
            channel = SKMSG_CTL_MSG_GET_CHANNEL(msg);
            list = rbopenlist(transfers);
            CHECK_ALLOC(list);
            while ((item = (transfer_t *)rbreadlist(list)) != NULL) {
                if (item->channel_exists && channel == item->channel) {
                    INFOMSG("Channel to %s died", item->ident);
                    item->disconnect = 1;
                    rv = transferUnblock(item);
                    if (rv != 0) {
                        threadExit(EXIT_FAILURE, NULL);
                    }
                    break;
                }
            }
            rbcloselist(list);

            sendString(control, channel, INTERNAL, CONN_DISCONNECT_RETRY,
                       LOG_INFO, "Remote side of channel died");
            break;

          default:
            WARNINGMSG("Received unknown control message %d", skMsgType(msg));
        }

        skMsgDestroy(msg);
    }

    DEBUG_PRINT1("client_main() thread ended");
    return NULL;
}


int startTransferDaemon(
    void)
{
    int rv;

    /* Initialize the message queue */
    rv = skMsgQueueCreate(&control);
    if (rv != 0) {
        skAppPrintErr("Failed to initialize message queue");
        exit(EXIT_FAILURE);
    }

#if SK_ENABLE_GNUTLS
    if (tls_ca_file) {
        rv = skMsgQueueAddCA(control, tls_ca_file);
        if (rv != 0) {
            CRITMSG("Invalid Certificate Authority file: %s", tls_ca_file);
            return -1;
        }
#ifdef PKCS12
        if (tls_pkcs12_file) {
            const char *password = getenv(password_env);
            if (password == NULL) {
                password = "";
            }
            rv = skMsgQueueAddPKCS12(control, tls_pkcs12_file, password);
            if (rv != 0) {
                CRITMSG("Invalid encryption cert file: %s", tls_pkcs12_file);
                return -1;
            }
        } else
#endif /* PKCS12 */
        {
            rv = skMsgQueueAddCert(control, tls_cert_file, tls_key_file);
            if (rv != 0) {
                CRITMSG("Invalid encryption cert or key file: %s, %s",
                        tls_cert_file, tls_key_file);
                return -1;
            }
        }
    }
#endif /* SK_ENABLE_GNUTLS */

    switch (mode) {
      case CLIENT:
        rv = skthread_create("client_main", &control_thread,
                             clientMain, NULL);
        if (rv != 0) {
            CRITMSG("Failed to create primary client thread: %s",strerror(rv));
            return -1;
        }
        break;
      case SERVER:
        rv = skthread_create("server_main", &control_thread,
                             serverMain, NULL);
        if (rv != 0) {
            CRITMSG("Failed to create primary server thread: %s",strerror(rv));
            return -1;
        }
        break;
      default:
        ASSERT_ABORT(0);
    }

    return 0;
}


int checkMsg(
    sk_msg_t           *msg,
    sk_msg_queue_t     *q,
    connection_msg_t    type)
{
    skm_type_t t;
    skm_len_t  len;

    assert(msg);
    assert(q);
    assert(type < CONN_NUMBER_OF_CONNECTION_MESSAGES);

    t = skMsgType(msg);

    if (t != type) {
        sendString(q, skMsgChannel(msg), EXTERNAL,
                   CONN_DISCONNECT, LOG_WARNING,
                   "Protocol error: expected %s, got %s (0x%04x)",
                   conn_msg_data[type].name,
                   ((t >= CONN_NUMBER_OF_CONNECTION_MESSAGES)
                    ? "<unknown>"
                    : conn_msg_data[t].name),
                   t);
        return 1;
    }

    if (conn_msg_data[type].size == -1) {
        return 0;
    }

    len = skMsgLength(msg);
    if (len != conn_msg_data[type].size) {
        sendString(q, skMsgChannel(msg), EXTERNAL,
                   CONN_DISCONNECT, LOG_WARNING,
                   "Protocol error: type %s, expected len %" PRId32 ", got %d",
                   conn_msg_data[type].name,
                   conn_msg_data[type].size, len);
        return 1;
    }

    return 0;
}


#undef sendString
int sendString(
    sk_msg_queue_t *q,
    skm_channel_t   channel,
    int             internal,
    skm_type_t      type,
    int             loglevel,
    const char     *fmt,
    ...)
{
    int rv;
    va_list args;
    char msg[MAX_ERROR_MESSAGE];
    int len;

    va_start(args, fmt);
    len = vsnprintf(msg, sizeof(msg), fmt, args);
    if (len >= (int)sizeof(msg)) {
        len = sizeof(msg) - 1;
        msg[len] = '\0';
    }

    if (internal) {
        rv = skMsgQueueInjectMessage(q, channel, type, msg, len + 1);
    } else {
        rv = skMsgQueueSendMessage(q, channel, type, msg, len + 1);
    }

    if (!internal) {
        sklog(loglevel, "Sending \"%s\"", msg);
    }
    return rv;
}


void threadExit(
    int             status,
    void           *retval)
{
    DEBUG_PRINT1("threadExit called");
    main_retval = status;
    pthread_kill(main_thread, SIGQUIT);
    pthread_exit(retval);
}


/*
** Local Variables:
** mode:c
** indent-tabs-mode:nil
** c-basic-offset:4
** End:
*/

[-- Attachment #3: Type: text/plain, Size: 41 bytes --]


-- 
Michael Welsh Duggan
(mwd@cert.org)

  reply	other threads:[~2013-01-17 16:28 UTC|newest]

Thread overview: 55+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2012-06-19 20:45 bug#11749: 24.1; C-mode indentation gives wrong-type-argument error Kim F. Storm
2012-06-21  7:17 ` Glenn Morris
2012-06-21  9:34   ` Kim Storm
2012-08-28 16:17 ` bug#11749: 24.2; wrong-type-argument Kim F. Storm
     [not found] ` <handler.11749.B.13401389485673.ack@debbugs.gnu.org>
2012-08-28 22:49   ` bug#11749: Acknowledgement (24.1; C-mode indentation gives wrong-type-argument error.) Kim Storm
2012-08-31 11:01     ` Eli Zaretskii
2012-08-31 12:37       ` Kim Storm
2012-09-02 21:16     ` Alan Mackenzie
2012-09-03  9:47       ` Kim Storm
2012-09-03 13:56         ` Stefan Monnier
2012-09-03 14:20           ` Kim Storm
2012-09-03 18:52             ` Stefan Monnier
2012-09-05 20:48         ` Alan Mackenzie
2012-09-07  3:45           ` Michael Welsh Duggan
2012-09-07 14:53             ` Stefan Monnier
2012-09-07 16:16               ` Kim Storm
2012-09-08 21:14             ` Alan Mackenzie
2012-09-10 12:18               ` Michael Welsh Duggan
2012-09-10 12:48                 ` Michael Welsh Duggan
2012-09-21 17:47                   ` Michael Welsh Duggan
2012-10-07 10:59                     ` Alan Mackenzie
2012-10-09 14:05                       ` Michael Welsh Duggan
2012-10-10 20:00                         ` Alan Mackenzie
2012-10-14 17:06                           ` Alan Mackenzie
2012-10-23 16:13                             ` Michael Welsh Duggan
2012-10-25 13:41                             ` Michael Welsh Duggan
2012-10-28 11:36                               ` Alan Mackenzie
2012-11-04  3:43                                 ` Chong Yidong
2012-11-04 20:42                                   ` Alan Mackenzie
2012-11-21 20:58                                   ` Alan Mackenzie
2012-11-22 14:52                                     ` Stefan Monnier
2012-11-04 20:39                                 ` Alan Mackenzie
2012-11-04 21:04                                   ` Kim Storm
2012-11-14 16:52                                   ` Michael Welsh Duggan
2012-11-21 21:33                                     ` Alan Mackenzie
2012-11-26 13:25                                       ` Michael Welsh Duggan
2012-12-10  3:35                                         ` Michael Welsh Duggan
2013-01-07 12:09                                           ` Alan Mackenzie
2013-01-17 16:27                                             ` Michael Welsh Duggan
2013-01-17 16:28                                               ` Michael Welsh Duggan [this message]
2013-01-23 14:16                                               ` Alan Mackenzie
2013-01-23 15:39                                                 ` Michael Welsh Duggan
2013-01-29 11:37                                                   ` Alan Mackenzie
     [not found]                                                   ` <20130129113737.GA4544@acm.acm>
2013-02-01 22:18                                                     ` Michael Welsh Duggan
2013-02-01 23:50                                                       ` Kim Storm
2013-02-02 19:35                                                         ` Alan Mackenzie
2012-09-10 13:10                 ` Michael Welsh Duggan
2012-09-10 13:22                   ` Michael Welsh Duggan
2012-09-10 18:25                     ` Michael Welsh Duggan
2012-09-05 13:11 ` bug#11749: I also have this issue Denis Zalevskiy
2013-01-08 19:10 ` bug#11749: Acknowledgement (24.1; C-mode indentation gives wrong-type-argument error.) Glenn Morris
2013-01-09 22:13   ` Alan Mackenzie
2013-02-02 18:37 ` bug#11749: 24.1; C-mode indentation gives wrong-type-argument error Alan Mackenzie
2013-01-08 13:49   ` bug#13385: 24.1; TAB in C file causes type error Julian Stecklina
     [not found]     ` <handler.13385.D11749.1359830688892.notifdone@debbugs.gnu.org>
2013-02-03 13:00       ` bug#13385: closed (Re: 24.1; C-mode indentation gives wrong-type-argument error.) Julian Stecklina

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://www.gnu.org/software/emacs/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=tntbocnu63y.fsf@waterbuck.yellow.cert.org \
    --to=mwd@cert.org \
    --cc=11749@debbugs.gnu.org \
    --cc=acm@muc.de \
    --cc=storm@cua.dk \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).