예제 #1
0
파일: xfer-dcc.c 프로젝트: matsuu/weechat
void
xfer_dcc_send_file_child (struct t_xfer *xfer)
{
    int num_read, num_sent, blocksize;
    static char buffer[XFER_BLOCKSIZE_MAX];
    uint32_t ack;
    time_t last_sent, new_time, last_second;
    long sent_last_second;
    
    blocksize = xfer->blocksize;
    if (weechat_config_integer (xfer_config_network_speed_limit) > 0)
    {
        if (blocksize > weechat_config_integer (xfer_config_network_speed_limit) * 1024)
            blocksize = weechat_config_integer (xfer_config_network_speed_limit) * 1024;
    }
    
    last_sent = time (NULL);
    last_second = time (NULL);
    sent_last_second = 0;
    while (1)
    {
        /* read DCC ACK (sent by receiver) */
        if (xfer->pos > xfer->ack)
        {
            /* we should receive ACK for packets sent previously */
            while (1)
            {
                num_read = recv (xfer->sock, (char *) &ack, 4, MSG_PEEK);
                if ((num_read < 1) &&
                    ((num_read != -1) || (errno != EAGAIN)))
                {
                    xfer_network_write_pipe (xfer, XFER_STATUS_FAILED,
                                             XFER_ERROR_SEND_BLOCK);
                    return;
                }
                if (num_read == 4)
                {
                    recv (xfer->sock, (char *) &ack, 4, 0);
                    xfer->ack = ntohl (ack);
                    
                    /* DCC send ok? */
                    if ((xfer->pos >= xfer->size)
                        && (xfer->ack >= xfer->size))
                    {
                        xfer_network_write_pipe (xfer, XFER_STATUS_DONE,
                                                 XFER_NO_ERROR);
                        return;
                    }
                }
                else
                    break;
            }
        }
        
        /* send a block to receiver */
        if ((xfer->pos < xfer->size) &&
             (xfer->fast_send || (xfer->pos <= xfer->ack)))
        {
            if ((weechat_config_integer (xfer_config_network_speed_limit) > 0)
                && (sent_last_second >= weechat_config_integer (xfer_config_network_speed_limit) * 1024))
            {
                /* we're sending too fast (according to speed limit set by user) */
                usleep (100);
            }
            else
            {
                lseek (xfer->file, xfer->pos, SEEK_SET);
                num_read = read (xfer->file, buffer, blocksize);
                if (num_read < 1)
                {
                    xfer_network_write_pipe (xfer, XFER_STATUS_FAILED,
                                             XFER_ERROR_READ_LOCAL);
                    return;
                }
                num_sent = send (xfer->sock, buffer, num_read, 0);
                if (num_sent < 0)
                {
                    /* socket is temporarily not available (receiver can't receive
                       amount of data we sent ?!) */
                    if (errno == EAGAIN)
                        usleep (1000);
                    else
                    {
                        xfer_network_write_pipe (xfer, XFER_STATUS_FAILED,
                                                 XFER_ERROR_SEND_BLOCK);
                        return;
                    }
                }
                if (num_sent > 0)
                {
                    xfer->pos += (unsigned long) num_sent;
                    sent_last_second += (unsigned long) num_sent;
                    new_time = time (NULL);
                    if (last_sent != new_time)
                    {
                        last_sent = new_time;
                        xfer_network_write_pipe (xfer, XFER_STATUS_ACTIVE,
                                                 XFER_NO_ERROR);
                    }
                }
            }
        }
        else
            usleep (1000);
        
        if (time (NULL) > last_second)
        {
            last_second = time (NULL);
            sent_last_second = 0;
        }
    }
}
예제 #2
0
void
xfer_dcc_recv_file_child (struct t_xfer *xfer)
{
    int flags, num_read, ack_enabled, ready;
    static char buffer[XFER_BLOCKSIZE_MAX];
    time_t last_sent, new_time;
    unsigned long long pos_last_ack;
    fd_set read_fds, write_fds, except_fds;

    /* first connect to sender (blocking) */
    if (!weechat_network_connect_to (xfer->proxy, xfer->sock,
                                     xfer->remote_address, xfer->port))
    {
        xfer_network_write_pipe (xfer, XFER_STATUS_FAILED,
                                 XFER_ERROR_CONNECT_SENDER);
        return;
    }

    /* connection is OK, change DCC status (inform parent process) */
    xfer_network_write_pipe (xfer, XFER_STATUS_ACTIVE,
                             XFER_NO_ERROR);

    /* make socket non-blocking */
    flags = fcntl (xfer->sock, F_GETFL);
    if (flags == -1)
        flags = 0;
    fcntl (xfer->sock, F_SETFL, flags | O_NONBLOCK);

    last_sent = time (NULL);
    ack_enabled = 1;
    pos_last_ack = 0;

    while (1)
    {
        /* wait until there is something to read on socket (or error) */
        FD_ZERO (&read_fds);
        FD_ZERO (&write_fds);
        FD_ZERO (&except_fds);
        FD_SET (xfer->sock, &read_fds);
        ready = select (xfer->sock + 1, &read_fds, &write_fds, &except_fds, NULL);
        if (ready == 0)
        {
            xfer_network_write_pipe (xfer, XFER_STATUS_FAILED,
                                     XFER_ERROR_RECV_BLOCK);
            return;
        }

        /* read maximum data on socket (until nothing is available) */
        while (1)
        {
            num_read = recv (xfer->sock, buffer, sizeof (buffer), 0);
            if (num_read == -1)
            {
                if ((errno != EAGAIN) && (errno != EWOULDBLOCK) && (errno != EINTR))
                {
                    xfer_network_write_pipe (xfer, XFER_STATUS_FAILED,
                                             XFER_ERROR_RECV_BLOCK);
                    return;
                }
                /*
                 * no more data available on socket: exit loop, send ACK, and
                 * wait for new data on socket
                 */
                break;
            }
            else
            {
                if (num_read == 0)
                {
                    xfer_network_write_pipe (xfer, XFER_STATUS_FAILED,
                                             XFER_ERROR_RECV_BLOCK);
                    return;
                }

                if (write (xfer->file, buffer, num_read) == -1)
                {
                    xfer_network_write_pipe (xfer, XFER_STATUS_FAILED,
                                             XFER_ERROR_WRITE_LOCAL);
                    return;
                }

                xfer->pos += (unsigned long long) num_read;

                /* file received OK? */
                if (xfer->pos >= xfer->size)
                {
                    /*
                     * extra delay before sending ACK, otherwise the send of ACK
                     * may fail
                     */
                    usleep (100000);

                    /* send ACK to sender without checking return code (file OK) */
                    xfer_dcc_recv_file_send_ack (xfer);

                    /* set status done and return */
                    xfer_network_write_pipe (xfer, XFER_STATUS_DONE,
                                             XFER_NO_ERROR);
                    return;
                }

                /* update status of DCC (parent process) */
                new_time = time (NULL);
                if (last_sent != new_time)
                {
                    last_sent = new_time;
                    xfer_network_write_pipe (xfer, XFER_STATUS_ACTIVE,
                                             XFER_NO_ERROR);
                }
            }
        }

        /* send ACK to sender (if needed) */
        if (ack_enabled && (xfer->pos > pos_last_ack))
        {
            switch (xfer_dcc_recv_file_send_ack (xfer))
            {
            case 0:
                /* send error, socket down? */
                xfer_network_write_pipe (xfer, XFER_STATUS_FAILED,
                                         XFER_ERROR_SEND_ACK);
                return;
            case 1:
                /* send error, not fatal (buffer full?): disable ACKs */
                ack_enabled = 0;
                break;
            case 2:
                /* send OK: save position in file as last ACK sent */
                pos_last_ack = xfer->pos;
                break;
            }
        }
    }
}
예제 #3
0
파일: xfer-dcc.c 프로젝트: matsuu/weechat
void
xfer_dcc_recv_file_child (struct t_xfer *xfer)
{
    int num_read;
    static char buffer[XFER_BLOCKSIZE_MAX];
    uint32_t pos;
    time_t last_sent, new_time;
    
    /* first connect to sender (blocking) */
    if (!weechat_network_connect_to (xfer->proxy, xfer->sock,
                                     xfer->address, xfer->port))
    {
        xfer_network_write_pipe (xfer, XFER_STATUS_FAILED,
                                 XFER_ERROR_CONNECT_SENDER);
        return;
    }
    
    /* connection is ok, change DCC status (inform parent process) */
    xfer_network_write_pipe (xfer, XFER_STATUS_ACTIVE,
                             XFER_NO_ERROR);
    
    last_sent = time (NULL);
    while (1)
    {    
        num_read = recv (xfer->sock, buffer, sizeof (buffer), 0);
        if (num_read == -1)
        {
            /* socket is temporarily not available (sender is not fast ?!) */
            if ((errno == EAGAIN) || (errno == EINTR))
                usleep (1000);
            else
            {
                xfer_network_write_pipe (xfer, XFER_STATUS_FAILED,
                                         XFER_ERROR_RECV_BLOCK);
                return;
            }
        }
        else
        {
            if (num_read == 0)
            {
                xfer_network_write_pipe (xfer, XFER_STATUS_FAILED,
                                         XFER_ERROR_RECV_BLOCK);
                return;
            }
            
            if (write (xfer->file, buffer, num_read) == -1)
            {
                xfer_network_write_pipe (xfer, XFER_STATUS_FAILED,
                                         XFER_ERROR_WRITE_LOCAL);
                return;
            }
            
            xfer->pos += (unsigned long) num_read;
            pos = htonl (xfer->pos);
            
            /* we don't check return code, not a problem if an ACK send failed */
            send (xfer->sock, (char *) &pos, 4, 0);
            
            /* file received ok? */
            if (xfer->pos >= xfer->size)
            {
                xfer_network_write_pipe (xfer, XFER_STATUS_DONE,
                                         XFER_NO_ERROR);
                return;
            }
            
            new_time = time (NULL);
            if (last_sent != new_time)
            {
                last_sent = new_time;
                xfer_network_write_pipe (xfer, XFER_STATUS_ACTIVE,
                                         XFER_NO_ERROR);
            }
        }
    }
}
예제 #4
0
파일: xfer-dcc.c 프로젝트: walokra/weechat
void
xfer_dcc_recv_file_child (struct t_xfer *xfer)
{
    int flags, num_read, ack_enabled, ready;
    static char buffer[XFER_BLOCKSIZE_MAX];
    time_t last_sent, new_time;
    unsigned long long pos_last_ack;
    struct pollfd poll_fd;
    ssize_t written, total_written;
    unsigned char *bin_hash;
    char hash[9];

    /* if resuming, hash the portion of the file we have */
    if ((xfer->start_resume > 0) && xfer->hash_handle)
    {
        xfer_network_write_pipe (xfer, XFER_STATUS_HASHING,
                                 XFER_NO_ERROR);
        if (!xfer_dcc_resume_hash (xfer))
        {
            gcry_md_close (*xfer->hash_handle);
            free (xfer->hash_handle);
            xfer->hash_handle = NULL;
            xfer_network_write_pipe (xfer, XFER_STATUS_HASHING,
                                     XFER_ERROR_HASH_RESUME_ERROR);
        }
        xfer_network_write_pipe (xfer, XFER_STATUS_CONNECTING,
                                 XFER_NO_ERROR);
    }

    /* first connect to sender (blocking) */
    xfer->sock = weechat_network_connect_to (xfer->proxy,
                                             xfer->remote_address,
                                             xfer->remote_address_length);
    if (xfer->sock == -1)
    {
        xfer_network_write_pipe (xfer, XFER_STATUS_FAILED,
                                 XFER_ERROR_CONNECT_SENDER);
        return;
    }

    /* connection is OK, change DCC status (inform parent process) */
    xfer_network_write_pipe (xfer, XFER_STATUS_ACTIVE,
                             XFER_NO_ERROR);

    /* make socket non-blocking */
    flags = fcntl (xfer->sock, F_GETFL);
    if (flags == -1)
        flags = 0;
    fcntl (xfer->sock, F_SETFL, flags | O_NONBLOCK);

    last_sent = time (NULL);
    ack_enabled = 1;
    pos_last_ack = 0;

    while (1)
    {
        /* wait until there is something to read on socket (or error) */
        poll_fd.fd = xfer->sock;
        poll_fd.events = POLLIN;
        poll_fd.revents = 0;
        ready = poll (&poll_fd, 1, -1);
        if (ready <= 0)
        {
            xfer_network_write_pipe (xfer, XFER_STATUS_FAILED,
                                     XFER_ERROR_RECV_BLOCK);
            return;
        }

        /* read maximum data on socket (until nothing is available) */
        while (1)
        {
            num_read = recv (xfer->sock, buffer, sizeof (buffer), 0);
            if (num_read == -1)
            {
                if ((errno != EAGAIN) && (errno != EWOULDBLOCK) && (errno != EINTR))
                {
                    xfer_network_write_pipe (xfer, XFER_STATUS_FAILED,
                                             XFER_ERROR_RECV_BLOCK);
                    return;
                }
                /*
                 * no more data available on socket: exit loop, send ACK, and
                 * wait for new data on socket
                 */
                break;
            }
            else
            {
                if ((num_read == 0) && (xfer->pos < xfer->size))
                {
                    xfer_network_write_pipe (xfer, XFER_STATUS_FAILED,
                                             XFER_ERROR_RECV_BLOCK);
                    return;
                }

                /* bytes received, write to disk */
                total_written = 0;
                while (total_written < num_read)
                {
                    written = write (xfer->file,
                                     buffer + total_written,
                                     num_read - total_written);
                    if (written < 0)
                    {
                        if (errno == EINTR)
                            continue;
                        xfer_network_write_pipe (xfer, XFER_STATUS_FAILED,
                                                 XFER_ERROR_WRITE_LOCAL);
                        return;
                    }
                    else
                    {
                        if (xfer->hash_handle)
                        {
                            gcry_md_write (*xfer->hash_handle,
                                           buffer + total_written,
                                           written);
                        }
                        total_written += written;
                    }
                }

                xfer->pos += (unsigned long long) num_read;

                /* file received OK? */
                if (xfer->pos >= xfer->size)
                {
                    /* check hash and report result to pipe */
                    if (xfer->hash_handle)
                    {
                        gcry_md_final (*xfer->hash_handle);
                        bin_hash = gcry_md_read (*xfer->hash_handle, 0);
                        if (bin_hash)
                        {
                            snprintf (hash, sizeof (hash), "%.2X%.2X%.2X%.2X",
                                      bin_hash[0], bin_hash[1], bin_hash[2],
                                      bin_hash[3]);
                            if (weechat_strcasecmp (hash,
                                                    xfer->hash_target) == 0)
                            {
                                xfer_network_write_pipe (xfer,
                                                         XFER_STATUS_HASHED,
                                                         XFER_NO_ERROR);
                            }
                            else
                            {
                                xfer_network_write_pipe (xfer,
                                                         XFER_STATUS_HASHED,
                                                         XFER_ERROR_HASH_MISMATCH);
                            }
                        }
                    }

                    fsync (xfer->file);

                    /*
                     * extra delay before sending ACK, otherwise the send of ACK
                     * may fail
                     */
                    usleep (100000);

                    /* send ACK to sender without checking return code (file OK) */
                    xfer_dcc_recv_file_send_ack (xfer);

                    /* set status done and return */
                    xfer_network_write_pipe (xfer, XFER_STATUS_DONE,
                                             XFER_NO_ERROR);
                    return;
                }

                /* update status of DCC (parent process) */
                new_time = time (NULL);
                if (last_sent != new_time)
                {
                    last_sent = new_time;
                    xfer_network_write_pipe (xfer, XFER_STATUS_ACTIVE,
                                             XFER_NO_ERROR);
                }
            }
        }

        /* send ACK to sender (if needed) */
        if (ack_enabled && (xfer->pos > pos_last_ack))
        {
            switch (xfer_dcc_recv_file_send_ack (xfer))
            {
                case 0:
                    /* send error, socket down? */
                    xfer_network_write_pipe (xfer, XFER_STATUS_FAILED,
                                             XFER_ERROR_SEND_ACK);
                    return;
                case 1:
                    /* send error, not fatal (buffer full?): disable ACKs */
                    ack_enabled = 0;
                    break;
                case 2:
                    /* send OK: save position in file as last ACK sent */
                    pos_last_ack = xfer->pos;
                    break;
            }
        }
    }
}