static void libirc_dcc_process_descriptors (irc_session_t * ircsession, fd_set *in_set, fd_set *out_set) { irc_dcc_session_t * dcc; /* * We need to use such a complex scheme here, because on every callback * a number of DCC sessions could be destroyed. */ libirc_mutex_lock (&ircsession->mutex_dcc); for ( dcc = ircsession->dcc_sessions; dcc; dcc = dcc->next ) { if ( dcc->state == LIBIRC_STATE_LISTENING && FD_ISSET (dcc->sock, in_set) ) { socklen_t len = sizeof(dcc->remote_addr); int nsock, err = 0; // New connection is available; accept it. if ( socket_accept (&dcc->sock, &nsock, (struct sockaddr *) &dcc->remote_addr, &len) ) err = LIBIRC_ERR_ACCEPT; // On success, change the active socket and change the state if ( err == 0 ) { // close the listen socket, and replace it by a newly // accepted socket_close (&dcc->sock); dcc->sock = nsock; dcc->state = LIBIRC_STATE_CONNECTED; } // If this is DCC chat, inform the caller about accept() // success or failure. // Otherwise (DCC send) there is no reason. if ( dcc->dccmode == LIBIRC_DCC_CHAT ) { libirc_mutex_unlock (&ircsession->mutex_dcc); (*dcc->cb)(ircsession, dcc->id, err, dcc->ctx, 0, 0); libirc_mutex_lock (&ircsession->mutex_dcc); } if ( err ) libirc_dcc_destroy_nolock (ircsession, dcc->id); } if ( dcc->state == LIBIRC_STATE_CONNECTING && FD_ISSET (dcc->sock, out_set) ) { // Now we have to determine whether the socket is connected // or the connect is failed struct sockaddr_in saddr; socklen_t slen = sizeof(saddr); int err = 0; if ( getpeername (dcc->sock, (struct sockaddr*)&saddr, &slen) < 0 ) err = LIBIRC_ERR_CONNECT; // On success, change the state if ( err == 0 ) dcc->state = LIBIRC_STATE_CONNECTED; // If this is DCC chat, inform the caller about connect() // success or failure. // Otherwise (DCC send) there is no reason. if ( dcc->dccmode == LIBIRC_DCC_CHAT ) { libirc_mutex_unlock (&ircsession->mutex_dcc); (*dcc->cb)(ircsession, dcc->id, err, dcc->ctx, 0, 0); libirc_mutex_lock (&ircsession->mutex_dcc); } if ( err ) libirc_dcc_destroy_nolock (ircsession, dcc->id); } if ( dcc->state == LIBIRC_STATE_CONNECTED || dcc->state == LIBIRC_STATE_CONFIRM_SIZE ) { if ( FD_ISSET (dcc->sock, in_set) ) { int length, offset = 0, err = 0; unsigned int amount = sizeof (dcc->incoming_buf) - dcc->incoming_offset; length = socket_recv (&dcc->sock, dcc->incoming_buf + dcc->incoming_offset, amount); if ( length < 0 ) { err = LIBIRC_ERR_READ; } else if ( length == 0 ) { err = LIBIRC_ERR_CLOSED; if ( dcc->dccsend_file_fp ) { fclose (dcc->dccsend_file_fp); dcc->dccsend_file_fp = 0; } } else { dcc->incoming_offset += length; if ( dcc->dccmode != LIBIRC_DCC_CHAT ) offset = dcc->incoming_offset; else offset = libirc_findcrorlf (dcc->incoming_buf, dcc->incoming_offset); /* * In LIBIRC_STATE_CONFIRM_SIZE state we don't call any * callbacks (except there is an error). We just receive * the data, and compare it with the amount sent. */ if ( dcc->state == LIBIRC_STATE_CONFIRM_SIZE ) { if ( dcc->dccmode != LIBIRC_DCC_SENDFILE ) abort(); if ( dcc->incoming_offset == 4 ) { unsigned int received_size = ntohl (*((unsigned int*)dcc->incoming_buf)); // Sent size confirmed if ( dcc->file_confirm_offset == received_size ) { dcc->state = LIBIRC_STATE_CONNECTED; dcc->incoming_offset = 0; } else err = LIBIRC_ERR_WRITE; } } else { /* * If it is DCC_CHAT, we send a 0-terminated string * (which is smaller than offset). Otherwise we send * a full buffer. */ libirc_mutex_unlock (&ircsession->mutex_dcc); if ( dcc->dccmode != LIBIRC_DCC_CHAT ) { if ( dcc->dccmode != LIBIRC_DCC_RECVFILE ) abort(); (*dcc->cb)(ircsession, dcc->id, err, dcc->ctx, dcc->incoming_buf, offset); /* * If the session is not terminated in callback, * put the sent amount into the sent_packet_size_net_byteorder */ if ( dcc->state != LIBIRC_STATE_REMOVED ) { dcc->state = LIBIRC_STATE_CONFIRM_SIZE; dcc->file_confirm_offset += offset; *((unsigned int*)dcc->outgoing_buf) = htonl (dcc->file_confirm_offset); dcc->outgoing_offset = 4; } } else (*dcc->cb)(ircsession, dcc->id, err, dcc->ctx, dcc->incoming_buf, strlen(dcc->incoming_buf)); libirc_mutex_lock (&ircsession->mutex_dcc); if ( dcc->incoming_offset - offset > 0 ) memmove (dcc->incoming_buf, dcc->incoming_buf + offset, dcc->incoming_offset - offset); dcc->incoming_offset -= offset; } } /* * If error arises somewhere above, we inform the caller * of failure, and destroy this session. */ if ( err ) { libirc_mutex_unlock (&ircsession->mutex_dcc); (*dcc->cb)(ircsession, dcc->id, err, dcc->ctx, 0, 0); libirc_mutex_lock (&ircsession->mutex_dcc); libirc_dcc_destroy_nolock (ircsession, dcc->id); } } /* * Session might be closed (with sock = -1) after the in_set * processing, so before out_set processing we should check * for this case */ if ( dcc->state == LIBIRC_STATE_REMOVED ) continue; /* * Write bit set - we can send() something, and it won't block. */ if ( FD_ISSET (dcc->sock, out_set) ) { int length, offset, err = 0; /* * Because in some cases outgoing_buf could be changed * asynchronously (by another thread), we should lock * it. */ libirc_mutex_lock (&dcc->mutex_outbuf); offset = dcc->outgoing_offset; if ( offset > 0 ) { length = socket_send (&dcc->sock, dcc->outgoing_buf, offset); if ( length < 0 ) err = LIBIRC_ERR_WRITE; else if ( length == 0 ) err = LIBIRC_ERR_CLOSED; else { /* * If this was DCC_SENDFILE, and we just sent a packet, * change the state to wait for confirmation (and store * sent packet size) */ if ( dcc->state == LIBIRC_STATE_CONNECTED && dcc->dccmode == LIBIRC_DCC_SENDFILE ) { dcc->file_confirm_offset += offset; dcc->state = LIBIRC_STATE_CONFIRM_SIZE; libirc_mutex_unlock (&ircsession->mutex_dcc); libirc_mutex_unlock (&dcc->mutex_outbuf); (*dcc->cb)(ircsession, dcc->id, err, dcc->ctx, 0, offset); libirc_mutex_lock (&ircsession->mutex_dcc); libirc_mutex_lock (&dcc->mutex_outbuf); } if ( dcc->outgoing_offset - length > 0 ) memmove (dcc->outgoing_buf, dcc->outgoing_buf + length, dcc->outgoing_offset - length); dcc->outgoing_offset -= length; /* * If we just sent the confirmation data, change state * back. */ if ( dcc->state == LIBIRC_STATE_CONFIRM_SIZE && dcc->dccmode == LIBIRC_DCC_RECVFILE && dcc->outgoing_offset == 0 ) { /* * If the file is already received, we should inform * the caller, and close the session. */ if ( dcc->received_file_size == dcc->file_confirm_offset ) { libirc_mutex_unlock (&ircsession->mutex_dcc); libirc_mutex_unlock (&dcc->mutex_outbuf); (*dcc->cb)(ircsession, dcc->id, 0, dcc->ctx, 0, 0); libirc_dcc_destroy_nolock (ircsession, dcc->id); } else { /* Continue to receive the file */ dcc->state = LIBIRC_STATE_CONNECTED; } } } } libirc_mutex_unlock (&dcc->mutex_outbuf); /* * If error arises somewhere above, we inform the caller * of failure, and destroy this session. */ if ( err ) { libirc_mutex_unlock (&ircsession->mutex_dcc); (*dcc->cb)(ircsession, dcc->id, err, dcc->ctx, 0, 0); libirc_mutex_lock (&ircsession->mutex_dcc); libirc_dcc_destroy_nolock (ircsession, dcc->id); } } } } libirc_mutex_unlock (&ircsession->mutex_dcc); }
static void libirc_dcc_process_descriptors (irc_session_t * ircsession, fd_set *in_set, fd_set *out_set) { irc_dcc_session_t * dcc; /* * We need to use such a complex scheme here, because on every callback * a number of DCC sessions could be destroyed. */ libirc_mutex_lock (&ircsession->mutex_dcc); for ( dcc = ircsession->dcc_sessions; dcc; dcc = dcc->next ) { if ( dcc->state == LIBIRC_STATE_LISTENING && FD_ISSET (dcc->sock, in_set) ) { socklen_t len = sizeof(dcc->remote_addr); int nsock, err = 0; // New connection is available; accept it. if ( socket_accept (&dcc->sock, (socket_t*)&nsock, (struct sockaddr *) &dcc->remote_addr, &len) ) err = LIBIRC_ERR_ACCEPT; // On success, change the active socket and change the state if ( err == 0 ) { // close the listen socket, and replace it by a newly // accepted socket_close (&dcc->sock); dcc->sock = nsock; dcc->state = LIBIRC_STATE_CONNECTED; } // If this is DCC chat, inform the caller about accept() // success or failure. // Otherwise (DCC send) there is no reason. if ( dcc->dccmode == LIBIRC_DCC_CHAT ) { libirc_mutex_unlock (&ircsession->mutex_dcc); (*dcc->cb)(ircsession, dcc->id, err, dcc->ctx, 0, 0); libirc_mutex_lock (&ircsession->mutex_dcc); } if ( err ) libirc_dcc_destroy_nolock (ircsession, dcc->id); } if ( dcc->state == LIBIRC_STATE_CONNECTING && FD_ISSET (dcc->sock, out_set) ) { // Now we have to determine whether the socket is connected // or the connect is failed struct sockaddr_in saddr; socklen_t slen = sizeof(saddr); int err = 0; if ( getpeername (dcc->sock, (struct sockaddr*)&saddr, &slen) < 0 ) err = LIBIRC_ERR_CONNECT; // On success, change the state if ( err == 0 ) dcc->state = LIBIRC_STATE_CONNECTED; // If this is DCC chat, inform the caller about connect() // success or failure. // Otherwise (DCC send) there is no reason. if ( dcc->dccmode == LIBIRC_DCC_CHAT ) { libirc_mutex_unlock (&ircsession->mutex_dcc); (*dcc->cb)(ircsession, dcc->id, err, dcc->ctx, 0, 0); libirc_mutex_lock (&ircsession->mutex_dcc); } if ( err ) libirc_dcc_destroy_nolock (ircsession, dcc->id); } if ( dcc->state == LIBIRC_STATE_CONNECTED || dcc->state == LIBIRC_STATE_CONFIRM_SIZE ) { if ( FD_ISSET (dcc->sock, in_set) ) { int length, offset = 0, err = 0; unsigned int amount = sizeof (dcc->incoming_buf) - dcc->incoming_offset; length = socket_recv (&dcc->sock, dcc->incoming_buf + dcc->incoming_offset, amount); if ( length < 0 ) { err = LIBIRC_ERR_READ; } else if ( length == 0 ) { err = LIBIRC_ERR_CLOSED; if ( dcc->dccsend_file_fp ) { fclose (dcc->dccsend_file_fp); dcc->dccsend_file_fp = 0; } } else { dcc->incoming_offset += length; if ( dcc->dccmode != LIBIRC_DCC_CHAT ) offset = dcc->incoming_offset; else offset = libirc_findcrorlf (dcc->incoming_buf, dcc->incoming_offset); /* * In LIBIRC_STATE_CONFIRM_SIZE state we don't call any * callbacks (except there is an error). We just receive * the data, and compare it with the amount sent. */ if ( dcc->state == LIBIRC_STATE_CONFIRM_SIZE ) { if ( dcc->dccmode != LIBIRC_DCC_SENDFILE ) abort(); if ( dcc->incoming_offset == 4 ) { // The order is big-endian const unsigned char * bptr = (const unsigned char *) dcc->incoming_buf; unsigned int received_size = (bptr[0] << 24) | (bptr[1] << 16) | (bptr[2] << 8) | bptr[3]; // Sent size confirmed if ( dcc->file_confirm_offset == received_size ) { dcc->state = LIBIRC_STATE_CONNECTED; dcc->incoming_offset = 0; } else err = LIBIRC_ERR_WRITE; } } else { /* * If it is DCC_CHAT, we send a 0-terminated string * (which is smaller than offset). Otherwise we send * a full buffer. */ libirc_mutex_unlock (&ircsession->mutex_dcc); if ( dcc->dccmode != LIBIRC_DCC_CHAT ) { if ( dcc->dccmode != LIBIRC_DCC_RECVFILE ) abort(); (*dcc->cb)(ircsession, dcc->id, err, dcc->ctx, dcc->incoming_buf, offset); /* * If the session is not terminated in callback, * put the sent amount into the sent_packet_size_net_byteorder */ if ( dcc->state != LIBIRC_STATE_REMOVED ) { dcc->state = LIBIRC_STATE_CONFIRM_SIZE; dcc->file_confirm_offset += offset; // Store as big endian dcc->outgoing_buf[0] = (char) dcc->file_confirm_offset >> 24; dcc->outgoing_buf[1] = (char) dcc->file_confirm_offset >> 16; dcc->outgoing_buf[2] = (char) dcc->file_confirm_offset >> 8; dcc->outgoing_buf[3] = (char) dcc->file_confirm_offset; dcc->outgoing_offset = 4; } } else (*dcc->cb)(ircsession, dcc->id, err, dcc->ctx, dcc->incoming_buf, strlen(dcc->incoming_buf)); libirc_mutex_lock (&ircsession->mutex_dcc); if ( dcc->incoming_offset - offset > 0 ) memmove (dcc->incoming_buf, dcc->incoming_buf + offset, dcc->incoming_offset - offset); dcc->incoming_offset -= offset; } } /* * If error arises somewhere above, we inform the caller * of failure, and destroy this session. */ if ( err ) { libirc_mutex_unlock (&ircsession->mutex_dcc); (*dcc->cb)(ircsession, dcc->id, err, dcc->ctx, 0, 0); libirc_mutex_lock (&ircsession->mutex_dcc); libirc_dcc_destroy_nolock (ircsession, dcc->id); } }