int xfer_network_connect (struct t_xfer *xfer) { if (xfer->type == XFER_TYPE_CHAT_SEND) xfer->status = XFER_STATUS_WAITING; else xfer->status = XFER_STATUS_CONNECTING; if (xfer->sock < 0) { xfer->sock = socket (AF_INET, SOCK_STREAM, 0); if (xfer->sock < 0) return 0; } if (XFER_IS_SEND(xfer->type)) { /* listen to socket */ if (fcntl (xfer->sock, F_SETFL, O_NONBLOCK) == -1) return 0; if (listen (xfer->sock, 1) == -1) return 0; if (fcntl (xfer->sock, F_SETFL, 0) == -1) return 0; xfer->hook_fd = weechat_hook_fd (xfer->sock, 1, 0, 0, &xfer_network_fd_cb, xfer); /* add timeout */ if (weechat_config_integer (xfer_config_network_timeout) > 0) { xfer->hook_timer = weechat_hook_timer (weechat_config_integer (xfer_config_network_timeout) * 1000, 0, 1, &xfer_network_timer_cb, xfer); } } /* for chat receiving, connect to listening host */ if (xfer->type == XFER_TYPE_CHAT_RECV) { if (fcntl (xfer->sock, F_SETFL, O_NONBLOCK) == -1) return 0; weechat_network_connect_to (xfer->proxy, xfer->sock, xfer->address, xfer->port); xfer->hook_fd = weechat_hook_fd (xfer->sock, 1, 0, 0, &xfer_chat_recv_cb, xfer); } /* for file receiving, connection is made in child process (blocking) */ return 1; }
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); } } } }
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; } } } }
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; } } } }