Beispiel #1
0
static void
write_cb(int fd, void *arg)
{
    struct argos_net_conn *conn = arg;

    if (conn->state == ARGOS_NET_CONN_CONNECTING) {
        handle_connect(conn);
    }
    else if (conn->state == ARGOS_NET_CONN_CONNECTED) {
        /* this callback shouldn't happen unless outbuf is non-empty */
        size_t datalen = buffer_len(conn->outbuf);
        assert(datalen > 0);

        /*
         * when possible, we want to make sure to feed send() large blocks of
         * data at a time, so if there is only a little data in the outbuf, try
         * to get some more by compressing and moving over some of the pktbuf
         */
        if (datalen < SEND_SOFT_MIN) {
            (void) compress_and_xfer(conn, 0 /* don't force */);
            datalen = buffer_len(conn->outbuf);
        }

        ssize_t len = socket_send(conn, buffer_head(conn->outbuf), datalen);
        if (len != -1) {
            if (buffer_discard(conn->outbuf, len) == -1)
                KABOOM("buffer_discard");

            /*
             * When a partial-send occurs, this means we might have sent part of
             * a message and left the remainder sitting at the head of the
             * outbuf.  This is not a problem for the server (it will receive
             * and buffer the portion that was sent, waiting for us to send the
             * remainder) - however, it means that we cannot arbitrarily send
             * things down the socket until we finish off this partially-sent
             * message.  We call this state "unsynced" because we don't know if
             * we stopped sending on a message boundary or not.
             */
            if (buffer_len(conn->outbuf) == 0)
                conn->outbuf_unsync = 0;
            else if (datalen != len)
                conn->outbuf_unsync = 1;

            /* if connection is shutting down, check if buffers are now empty */
            if (conn->shutdown && buffers_are_empty(conn))
                kill_connection(conn);
        }
    }
}
Beispiel #2
0
static void s_WriteToSocket(CSocket& sock,
        const char* output_buffer, size_t output_buffer_size)
{
    size_t bytes_written;

    while (output_buffer_size > 0) {
        EIO_Status  status = sock.Write(output_buffer,
                output_buffer_size, &bytes_written);
        if (status != eIO_Success) {
            // Error writing to the socket.
            // Log what we can.
            string message_start;

            if (output_buffer_size > 32) {
                CTempString buffer_head(output_buffer, 32);
                message_start = NStr::PrintableString(buffer_head);
                message_start += " (TRUNCATED)";
            } else {
                CTempString buffer_head(output_buffer, output_buffer_size);
                message_start = NStr::PrintableString(buffer_head);
            }

            NCBI_THROW_FMT(CNetStorageException, eIOError,
                    "Error writing message to the NetStorage server " <<
                            sock.GetPeerAddress() << ". "
                    "Socket write error status: " <<
                            IO_StatusStr(status) << ". "
                    "Bytes written: " <<
                            NStr::NumericToString(bytes_written) << ". "
                    "Message begins with: " << message_start);
        }

        output_buffer += bytes_written;
        output_buffer_size -= bytes_written;
    }
}
Beispiel #3
0
static GstFlowReturn
send_buffers_before(AudioTrim *filter, gint64 before)
{
  GstFlowReturn ret = GST_FLOW_OK;
  GList *b = filter->buffers;
  while(b) {
    GstBuffer *buf = b->data;
    b->data = NULL;
    if ((gint64)GST_BUFFER_OFFSET(buf) >= before) {
      gst_buffer_unref(buf);
    } else {
      if ((gint64)GST_BUFFER_OFFSET_END(buf) > before) {
	GstBuffer *head = buffer_head(filter, buf, before);
	gst_buffer_unref(buf);
	buf = head;
      }
      ret = gst_pad_push(filter->srcpad, buf);
      if (ret != GST_FLOW_OK) break;
    }
    b = g_list_next(b);
  }
  release_buffers(filter);
  return ret;
}
Beispiel #4
0
static void
process_inbuf(struct argos_net_conn *conn)
{
    while (buffer_len(conn->inbuf) >= sizeof(struct argos_net_minimal_msg)) {
        struct argos_net_minimal_msg *header =
            (struct argos_net_minimal_msg *)buffer_head(conn->inbuf);

        uint16_t msgtype = ntohs(header->msgtype);
        uint32_t msglen = ntohl(header->msglen);

        /* check that message type and length are valid */
        if (ARGOS_NET_VALIDATE_MSGTYPE(msgtype) == 0) {
            orion_log_crit("invalid message type received; type=%hu, len=%u",
                msgtype, msglen);
            reset_connection(conn, 1 /* flush buffers */);
            return;
        }

        if (ARGOS_NET_VALIDATE_MSGLEN(msgtype, msglen) == 0) {
            orion_log_crit("invalid message len received; type=%hu, len=%u",
                msgtype, msglen);
            reset_connection(conn, 1 /* flush buffers */);
            return;
        }

        if (msglen > buffer_len(conn->inbuf)) {
            /* complete message not yet received */
            if (msglen > buffer_size(conn->inbuf)) {
                /* error - message is bigger than the entire inbuf */
                orion_log_err("inbuf too small for msgtype %hu (len=%u)",
                    msgtype, msglen);
                reset_connection(conn, 1 /* flush buffers */);
                return;
            }

            /* wait for more bytes to arrive on socket */
            break;
        }

        /* ok great, message length must be valid - process it */
        switch (msgtype) {
        case ARGOS_NET_CLOSECONN_MSGTYPE: {
            struct argos_net_closeconn_msg msg;
            int rv = buffer_read(conn->inbuf, &msg, sizeof(msg));
            assert(rv >= 0);

            orion_log_info("received close-connection message from server");
            reset_connection(conn, 1 /* flush buffers */);
            break;
        }

        case ARGOS_NET_ERROR_MSGTYPE: {
            struct argos_net_error_msg msg;
            int rv = buffer_read(conn->inbuf, &msg, sizeof(msg));
            assert(rv >= 0);

            char buf[ARGOS_NET_MAX_ERR_LEN+1];
            ssize_t bodylen = msglen - sizeof(msg);
            rv = buffer_read(conn->inbuf, buf, bodylen);
            assert(rv >= 0);
            buf[bodylen] = '\0';

            if (conn->errhandler != NULL)
                conn->errhandler(ntohs(msg.errnum), buf, conn->errhandler_user);
            else
                orion_log_err("[server] %s (%s)", strerror(ntohs(msg.errnum)),
                    buf);
            break;
        }

        case ARGOS_NET_HANDSHAKE_MSGTYPE: {
            struct argos_net_handshake_msg msg;
            int rv = buffer_read(conn->inbuf, &msg, sizeof(msg));
            assert(rv >= 0);

            /* sniffers should never receive handshake messages */
            orion_log_crit("received handshake message from server");
            reset_connection(conn, 1 /* flush buffers */);
            break;
        }

        case ARGOS_NET_PCAP_MSGTYPE: {
            struct argos_net_pcap_msg msg;
            int rv = buffer_read(conn->inbuf, &msg, sizeof(msg));
            assert(rv >= 0);

            size_t bodylen = msglen - sizeof(msg);
            assert(bodylen == ntohl(msg.caplen));

            if (conn->pkthandler == NULL) {
                int rv = buffer_discard(conn->inbuf, bodylen);
                assert(rv >= 0);
                break;
            }

            struct pcap_pkthdr pcap_hdr;
            pcap_hdr.len = ntohl(msg.pktlen);
            pcap_hdr.caplen = ntohl(msg.caplen);
            pcap_hdr.ts.tv_sec = ntohl(msg.ts_sec);
            pcap_hdr.ts.tv_usec = ntohl(msg.ts_usec);

            /*
             * index directly into buffer if possible (i.e. if the data
             * doesn't wrap in the buffer)
             */
            if (buffer_len(conn->inbuf) >= bodylen) {
                conn->pkthandler(&pcap_hdr, buffer_head(conn->inbuf),
                    conn->pkthandler_user);
                buffer_discard(conn->inbuf, bodylen);
            } else {
                /* data wraps; need to copy into a temporary buffer */
                u_char tempbuf[ARGOS_NET_MAX_PKT_LEN];
                buffer_read(conn->inbuf, tempbuf, bodylen);
                conn->pkthandler(&pcap_hdr, tempbuf, conn->pkthandler_user);
            }
            break;
        }

        case ARGOS_NET_SETBPF_MSGTYPE: {
            struct argos_net_setbpf_msg msg;
            int rv = buffer_read(conn->inbuf, &msg, sizeof(msg));
            assert(rv >= 0);

            char buf[ARGOS_NET_MAX_BPF_LEN+1];
            size_t bodylen = msglen - sizeof(msg);
            rv = buffer_read(conn->inbuf, buf, bodylen);
            assert(rv >= 0);
            buf[bodylen] = '\0';

            if (conn->bpfhandler != NULL)
                conn->bpfhandler(buf, conn->bpfhandler_user);
            /* else, just discard and ignore */
            break;
        }

        case ARGOS_NET_SETCHAN_MSGTYPE: {
            struct argos_net_setchan_msg msg;
            int rv = buffer_read(conn->inbuf, &msg, sizeof(msg));
            assert(rv >= 0);

            if (conn->chanhandler != NULL)
                conn->chanhandler(ntohs(msg.chan), conn->chanhandler_user);
            /* else, just discard and ignore */
            break;
        }

        case ARGOS_NET_STARTCLICK_MSGTYPE: {
            struct argos_net_startclick_msg msg;
            int rv = buffer_read(conn->inbuf, &msg, sizeof(msg));
            assert(rv >= 0);

            size_t bodylen = msglen - sizeof(msg);
            char *click_conf = malloc(bodylen+1);
            if (click_conf == NULL) {
                char errmsg[256];
                snprintf(errmsg, sizeof(errmsg), "malloc(%d): %s", bodylen,
                    strerror(errno));

                orion_log_crit_errno("malloc()");
                argos_net_send_errmsg(conn, errno, errmsg);
                kill_connection(conn);
                return;
            }

            rv = buffer_read(conn->inbuf, click_conf, bodylen);
            assert(rv >= 0);
            click_conf[bodylen] = '\0';

            /* is this click config any different from the last one we got? */
            uint32_t key = ntohl(msg.key);
            if (key == conn->click_config_key) {
                /* keys match; we can ignore this message */
                orion_log_debug("click-configuration keys match (%d)", key);
            } else {
                /* keys don't match; need to run this new configuration */
                orion_log_debug("new click-configuration key: %d", key);
                conn->click_config_key = key;
                if (conn->clickhandler != NULL)
                    conn->clickhandler(click_conf, conn->clickhandler_user);
            }

            free(click_conf);
            break;
        }

        case ARGOS_NET_STATS_MSGTYPE: {
            struct argos_net_stats_msg msg;
            int rv = buffer_read(conn->inbuf, &msg, sizeof(msg));
            assert(rv >= 0);

            orion_log_warn("received stats message from server");
            break;
        }

        default:
            orion_log_crit("process_inbuf() of net.c out of sync with net.h;"
                " no switch case for msgtype %hu", msgtype);
            kill_connection(conn);
        }
    }
}
Beispiel #5
0
static int
compress_and_xfer(struct argos_net_conn *conn, u_char force)
{
    /*
     * if the packet-buf is empty, quit right off; this check is not necessary
     * for correctness, but its nice to check it early for efficiency and also
     * to avoid some spam in the logs (e.g. lots of 'compression timeout'
     * messages) since this case is quite common.
     */
    if (buffer_len(conn->pktbuf) == 0)
        return 0;

#if ARGOS_NET_USE_COMPRESSION == ARGOS_NET_COMPRESS_NONE
    size_t to_xfer = min(buffer_len(conn->pktbuf),
        buffer_remaining(conn->outbuf));

    if (to_xfer == 0) return 0;

#if ARGOS_NET_TRACE_IO
    struct timeval start;
    if (gettimeofday(&start, NULL) != 0) {
        orion_log_crit_errno("gettimeofday");
        return 0;
    }
#endif /* #if ARGOS_NET_TRACE_IO */
    
    memcpy(buffer_tail(conn->outbuf), buffer_head(conn->pktbuf), to_xfer);

#if ARGOS_NET_TRACE_IO
    struct timeval end;
    if (gettimeofday(&end, NULL) != 0) {
        orion_log_crit_errno("gettimeofday");
        return 0;
    }

    struct timeval elapsed;
    orion_time_subtract(&end, &start, &elapsed);

    float elapsed_msec = elapsed.tv_sec*1000 + (float)elapsed.tv_usec/1000;

    orion_log_debug("memcpy'ed %u bytes in %.2f ms (%.2f MB/s)",
        to_xfer, elapsed_msec, ((to_xfer/elapsed_msec)*1000)/(1024*1024));
#endif /* #if ARGOS_NET_TRACE_IO */

    if (buffer_expand(conn->outbuf, to_xfer) < 0)
        KABOOM("buffer_expand");

    if (buffer_discard(conn->pktbuf, to_xfer) < 0)
        KABOOM("buffer_discard");

#else  /* #if ARGOS_NET_USE_COMPRESSION == ARGOS_NET_COMPRESS_NONE  */

    /*
     * In general, we want large blocks of packets to compress (because that
     * makes the compression more space-efficient).  So if there isn't much data
     * in pktbuf, just return without doing anything.  Note that we also need to
     * check to make sure there is enough room in the outbuf for us to put the
     * packets once they are compressed.
     */
    size_t minlen;

    if (force) {
        minlen = 1;
    } else {
        /*
         * If this conn has a small pktbuf (such that even with totally full,
         * the COMPRESS_HARD_MIN won't be met), then we need to adjust to a
         * limit that is actually attainable; we use 75% of the buffer size.
         */
        minlen = (3*buffer_size(conn->pktbuf))/4;

        /* usually, this is the value that we end up with for minlen: */
        minlen = min(minlen, COMPRESS_HARD_MIN);

        /*
         * one more special case: if argos_net_shutdown() was called on this
         * connection then there is no minimum compression size - we just want
         * to drain the buffers no matter how much is in there
         */
        if (conn->shutdown) minlen = 1;
    }

    /* quit if we don't have at least 'minlen' bytes of packet data to compress */
    if (buffer_len(conn->pktbuf) < minlen)
        return 0;

    /* check the total space available in the connection outbuf */
    size_t total_space = buffer_remaining(conn->outbuf);
    if (total_space < sizeof(struct argos_net_compress_msg))
        return 0;  /* not enough space available */

    /* this is the total space available for the compressed data */
    size_t space = total_space - sizeof(struct argos_net_compress_msg);

    /* don't exceed the maximum compression-block size */
    space = min(space, ARGOS_NET_MAX_COMPRESS_LEN);

    /*
     * given the space available, calculate how much packet data we can safely
     * consume (considering worst-cast input:output size ratios for whatever
     * compression algorithm we are using).
     */
    ssize_t ok_to_consume;

#if ARGOS_NET_USE_COMPRESSION == ARGOS_NET_COMPRESS_LZO
    /* this is the inversion of the function given in the LZO faq file */
    ok_to_consume = (16*(space - 64 - 3))/17;
#elif ARGOS_NET_USE_COMPRESSION == ARGOS_NET_COMPRESS_QUICKLZ
    /* this is the inversion of the function given in the QuickLZ manual */
    ok_to_consume = space - 400;
#else
    #error "unknown value for ARGOS_NET_USE_COMPRESSION"
#endif

    if (ok_to_consume <= 0) return 0;  /* not enough space available */

    /* number of bytes that will actually be compressed and transferred */
    size_t readlen = min(ok_to_consume, buffer_len(conn->pktbuf));
    assert(readlen > 0);

    /* don't exceed the maximum compression-block size */
    readlen = min(readlen, COMPRESS_HARD_MAX);

    /* where the compressed data should be written */
    u_char *write_ptr = buffer_tail(conn->outbuf) +
        sizeof(struct argos_net_compress_msg);

    struct argos_net_compress_msg *msg =
        (struct argos_net_compress_msg*)buffer_tail(conn->outbuf);
    msg->msgtype = htons(ARGOS_NET_COMPRESS_MSGTYPE);
    /* have to defer filling in msglen field */
    msg->algorithm = ARGOS_NET_USE_COMPRESSION;
    msg->orig_len = htonl(readlen);

#if ARGOS_NET_TRACE_IO
    /* measure the elapsed process time to try to detect cpu starvation */
    struct itimerval itimer_start;
    bzero(&itimer_start, sizeof(itimer_start));
    itimer_start.it_value.tv_sec = 100;  /* arbitrary large value */

    struct timeval start;
    if (gettimeofday(&start, NULL) != 0) {
        orion_log_crit_errno("gettimeofday");
        return 0;
    }

    if (setitimer(ITIMER_PROF, &itimer_start, NULL) != 0) {
        orion_log_crit_errno("setitimer");
        return 0;
    }
#endif /* #if ARGOS_NET_TRACE_IO */

#if ARGOS_NET_USE_COMPRESSION == ARGOS_NET_COMPRESS_LZO
    lzo_uint lzo_outlen;
    int rv = lzo1x_1_compress(buffer_head(conn->pktbuf), readlen,
        write_ptr, &lzo_outlen, conn->lzo_wrk_space);
    if (rv != LZO_E_OK) {
        /* according to LZO documentation "this should NEVER happen" */
        orion_log_crit("LZO compression library internal error: %d", rv);
        return 0;
    }

    uint32_t outlen = lzo_outlen;

#elif ARGOS_NET_USE_COMPRESSION == ARGOS_NET_COMPRESS_QUICKLZ
    uint32_t outlen = qlz_compress(buffer_head(conn->pktbuf), (char*)write_ptr, readlen, conn->qlz_scratch);
#else
    #error "unknown value for ARGOS_NET_USE_COMPRESSION"
#endif

#if ARGOS_NET_TRACE_IO
    /* call this before gettimeofday */
    struct itimerval itimer_end;
    if (getitimer(ITIMER_PROF, &itimer_end) != 0) {
        orion_log_crit_errno("getitimer");
        return 0;
    }
#endif /* #if ARGOS_NET_TRACE_IO */

    struct timeval end;
    if (gettimeofday(&end, NULL) != 0) {
        orion_log_crit_errno("gettimeofday");
        return 0;
    }

#if ARGOS_NET_TRACE_IO
    struct timeval real_elapsed;
    orion_time_subtract(&end, &start, &real_elapsed);

    float real_msec = real_elapsed.tv_sec*1000 +
        (float)real_elapsed.tv_usec/1000;

    struct timeval process_elapsed;
    orion_time_subtract(&itimer_start.it_value, &itimer_end.it_value,
        &process_elapsed);

    float process_msec = process_elapsed.tv_sec*1000 +
        (float)process_elapsed.tv_usec/1000;

    orion_log_debug("compressed %u bytes to %u (%.2f%%) in %.2f ms"
        " (%.2f MB/s); %.2f ms process time", readlen, outlen,
        (float)outlen*100/readlen, real_msec,
        ((readlen/real_msec)*1000)/(1024*1024), process_msec);
#endif /* #if ARGOS_NET_TRACE_IO */

    size_t total_len = sizeof(struct argos_net_compress_msg) + outlen;
    if (buffer_expand(conn->outbuf, total_len) < 0)
        KABOOM("buffer_expand");

    if (buffer_discard(conn->pktbuf, readlen) < 0)
        KABOOM("buffer_discard");

    /* write back into the msglen field now that we know the total length */
    msg->msglen = htonl(sizeof(struct argos_net_compress_msg) + outlen);

    /* check for incompressible block (this is normal for small blocks) */
    if (outlen > readlen) {
        if (readlen < 4096)
            orion_log_debug("incompressible block: inlen=%d, outlen=%d", readlen,
                outlen);
        else
            orion_log_warn("incompressible block: inlen=%d, outlen=%d", readlen,
                outlen);
    }
#endif  /* #if ARGOS_NET_USE_COMPRESSION == ARGOS_NET_COMPRESS_NONE  */

    /* cancel any currently schedule compression timeout event */
    if (conn->compress_evt_reg != NULL) {
        if (async_cancel(conn->compress_evt_reg) != 0)
            orion_log_crit_errno("async_cancel");
        conn->compress_evt_reg = NULL;
    }

    return 1;
}