/* ** send_queued_write ** This is called when there is a chance that some output would ** be possible. This attempts to empty the send queue as far as ** possible, and then if any data is left, a write is rescheduled. */ void send_queued_write(struct Client *to) { int retlen; #ifndef NDEBUG struct hook_io_data hdata; #endif struct dbuf_block *first; /* ** Once socket is marked dead, we cannot start writing to it, ** even if the error is removed... */ if (IsDead(to) || IsSendqBlocked(to)) return; /* no use calling send() now */ /* Next, lets try to write some data */ if (dbuf_length(&to->localClient->buf_sendq)) { #ifndef NDEBUG hdata.connection = to; #endif do { first = to->localClient->buf_sendq.blocks.head->data; #ifdef HAVE_LIBCRYPTO if(IsSSL(to)) { retlen = safe_SSL_write(to, first->data, first->size); printf("safe_ssl_write: writing: %s\n", first->data); printf("safe_ssl_write: %d written\n", retlen); if (retlen <= 0) break; } else #endif if ((retlen = send(to->localClient->fd, first->data, first->size, 0)) <= 0) break; #ifndef NDEBUG hdata.data = ((struct dbuf_block *) to->localClient->buf_sendq.blocks.head->data)->data; hdata.len = retlen; hook_call_event("iosend", &hdata); #endif dbuf_delete(&to->localClient->buf_sendq, retlen); /* We have some data written .. update counters */ to->localClient->sendB += retlen; me.localClient->sendB += retlen; if (to->localClient->sendB > 1023) { to->localClient->sendK += (to->localClient->sendB >> 10); to->localClient->sendB &= 0x03ff; /* 2^10 = 1024, 3ff = 1023 */ } else if (me.localClient->sendB > 1023) { me.localClient->sendK += (me.localClient->sendB >> 10); me.localClient->sendB &= 0x03ff; }
DLLFUNC CMD_FUNC(m_starttls) { if (!MyConnect(sptr) || !IsUnknown(sptr)) return 0; #ifndef USE_SSL /* sendnotice(sptr, "This server does not support SSL"); */ /* or numeric 691? */ /* actually... it's probably best to just act like we don't know this command...? */ sendto_one(sptr, err_str(ERR_NOTREGISTERED), me.name, "STARTTLS"); return 0; #else if (iConf.ssl_options & SSLFLAG_NOSTARTTLS) { sendto_one(sptr, err_str(ERR_NOTREGISTERED), me.name, "STARTTLS"); return 0; } if (IsSecure(sptr)) { sendto_one(sptr, err_str(ERR_STARTTLS), me.name, !BadPtr(sptr->name) ? sptr->name : "*", "STARTTLS failed. Already using TLS."); return 0; } dbuf_delete(&sptr->recvQ, 1000000); /* Clear up any remaining plaintext commands */ sendto_one(sptr, rpl_str(RPL_STARTTLS), me.name, !BadPtr(sptr->name) ? sptr->name : "*"); send_queued(sptr); SetSSLStartTLSHandshake(sptr); Debug((DEBUG_DEBUG, "Starting SSL handshake (due to STARTTLS) for %s", sptr->sockhost)); if ((sptr->ssl = SSL_new(ctx_server)) == NULL) goto fail; sptr->flags |= FLAGS_SSL; SSL_set_fd(sptr->ssl, sptr->fd); SSL_set_nonblocking(sptr->ssl); if (!ircd_SSL_accept(sptr, sptr->fd)) { Debug((DEBUG_DEBUG, "Failed SSL accept handshake in instance 1: %s", sptr->sockhost)); SSL_set_shutdown(sptr->ssl, SSL_RECEIVED_SHUTDOWN); SSL_smart_shutdown(sptr->ssl); SSL_free(sptr->ssl); goto fail; } /* HANDSHAKE IN PROGRESS */ return 0; fail: /* Failure */ sendto_one(sptr, err_str(ERR_STARTTLS), me.name, !BadPtr(sptr->name) ? sptr->name : "*", "STARTTLS failed"); sptr->ssl = NULL; sptr->flags &= ~FLAGS_SSL; SetUnknown(sptr); return 0; #endif }
/** Copy a single line from a data buffer. * If the output buffer cannot hold the whole line, or if there is no * EOL in the buffer, return 0. * @param[in,out] dyn Data buffer to copy from. * @param[out] buf Buffer to copy to. * @param[in] length Maximum number of bytes to copy. * @return Number of bytes copied to \a buf. */ unsigned int dbuf_getmsg(struct DBuf *dyn, char *buf, unsigned int length) { struct DBufBuffer *db; char *start; char *end; unsigned int count; unsigned int copied = 0; assert(0 != dyn); assert(0 != buf); if (0 == dbuf_flush(dyn)) return 0; assert(0 != dyn->head); db = dyn->head; start = db->start; assert(start < db->end); if (length > dyn->length) length = dyn->length; /* * might as well copy it while we're here */ while (length > 0) { end = IRCD_MIN(db->end, (start + length)); while (start < end && !IsEol(*start)) *buf++ = *start++; count = start - db->start; if (start < end) { *buf = '\0'; copied += count; dbuf_delete(dyn, copied); dbuf_flush(dyn); return copied; } if (0 == (db = db->next)) break; copied += count; length -= count; start = db->start; } return 0; }
size_t dbuf_get(struct DBuf* dyn, char* buf, size_t length) { size_t moved = 0; size_t chunk; const char* b; while (length > 0 && (b = dbuf_map(dyn, &chunk)) != 0) { if (chunk > length) chunk = length; memcpy(buf, b, chunk); dbuf_delete(dyn, chunk); buf += chunk; length -= chunk; moved += chunk; } return moved; }
/** Copy data from a buffer and remove what was copied. * @param[in,out] dyn Buffer to copy from. * @param[out] buf Buffer to write to. * @param[in] length Maximum number of bytes to copy. * @return Number of bytes actually copied. */ unsigned int dbuf_get(struct DBuf *dyn, char *buf, unsigned int length) { unsigned int moved = 0; unsigned int chunk; const char *b; assert(0 != dyn); assert(0 != buf); while (length > 0 && (b = dbuf_map(dyn, &chunk)) != 0) { if (chunk > length) chunk = length; memcpy(buf, b, chunk); dbuf_delete(dyn, chunk); buf += chunk; length -= chunk; moved += chunk; } return moved; }
/* ** send_queued ** This function is called from the main select-loop (or whatever) ** when there is a chance the some output would be possible. This ** attempts to empty the send queue as far as possible... */ int send_queued(aClient *to) { char *msg; int len, rlen, more = 0; aClient *bysptr = NULL; /* ** Once socket is marked dead, we cannot start writing to it, ** even if the error is removed... */ if (IsDead(to)) { /* ** Actually, we should *NEVER* get here--something is ** not working correct if send_queued is called for a ** dead socket... --msa */ return -1; } #ifdef ZIP_LINKS /* ** Here, we must make sure than nothing will be left in to->zip->outbuf ** This buffer needs to be compressed and sent if all the sendQ is sent */ if ((to->flags & FLAGS_ZIP) && to->zip->outcount) { if (DBufLength(&to->sendQ) > 0) more = 1; else { msg = zip_buffer(to, NULL, &len, 1); if (len == -1) return dead_link(to, "fatal error in zip_buffer()"); if (dbuf_put(&to->sendQ, msg, len) < 0) { to->exitc = EXITC_MBUF; return dead_link(to, "Buffer allocation error for %s", get_client_name(to, FALSE)); } } } #endif while (DBufLength(&to->sendQ) > 0 || more) { msg = dbuf_map(&to->sendQ, &len); /* Returns always len > 0 */ if ((rlen = deliver_it(to, msg, len)) < 0) { if ( (IsConnecting(to) || IsHandshake(to)) && to->serv && to->serv->byuid[0]) { bysptr = find_uid(to->serv->byuid, NULL); if (bysptr && !MyConnect(bysptr)) { sendto_one(bysptr, ":%s NOTICE %s :" "Write error (%s) to %s, closing link", ME, bysptr->name, strerror(-rlen), to->name); } } return dead_link(to, "Write error (%s) to %s, closing link", strerror(-rlen), get_client_name(to, FALSE)); } (void)dbuf_delete(&to->sendQ, rlen); to->lastsq = DBufLength(&to->sendQ)/1024; if (rlen < len) /* ..or should I continue until rlen==0? */ break; #ifdef ZIP_LINKS if (DBufLength(&to->sendQ) == 0 && more) { /* ** The sendQ is now empty, compress what's left ** uncompressed and try to send it too */ more = 0; msg = zip_buffer(to, NULL, &len, 1); if (len == -1) return dead_link(to, "fatal error in zip_buffer()"); if (dbuf_put(&to->sendQ, msg, len) < 0) { to->exitc = EXITC_MBUF; return dead_link(to, "Buffer allocation error for %s", get_client_name(to, FALSE)); } } #endif } return (IsDead(to)) ? -1 : 0; }