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); } } }
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; } }
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; }
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); } } }
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; }