📄 nut-transfer.c
字号:
svz_socket_t *sock; nut_header_t hdr; nut_push_t push; nut_packet_t *pkt; nut_transfer_t *trans; char *pushkey; struct sockaddr_in *addr = NULL; svz_portcfg_t *port; /* find original socket connection */ if ((sock = svz_sock_find (transfer->id, transfer->version)) != NULL) { /* create new gnutella header */ nut_calc_guid (hdr.id); hdr.function = NUT_PUSH_REQ; hdr.ttl = (svz_uint8_t) cfg->ttl; hdr.hop = 0; hdr.length = SIZEOF_NUT_PUSH; /* create push request */ memcpy (push.id, transfer->guid, NUT_GUID_SIZE); push.index = transfer->index; if ((port = svz_sock_portcfg (sock)) != NULL) addr = svz_portcfg_addr (port); push.ip = cfg->ip ? cfg->ip : addr ? addr->sin_addr.s_addr : sock->local_addr; push.port = (unsigned short) (cfg->port ? cfg->port : addr ? addr->sin_port : sock->local_port); /* create push request key and check if it was already sent */ pushkey = svz_malloc (16 + NUT_GUID_SIZE * 2); sprintf (pushkey, "%d:%s", push.index, nut_text_guid (push.id)); if ((trans = svz_hash_get (cfg->push, pushkey)) != NULL) {#if ENABLE_DEBUG svz_log (LOG_DEBUG, "nut: push request already sent\n");#endif svz_free (pushkey); return -1; } /* try sending header and push request */ if (svz_sock_write (sock, (char *) nut_put_header (&hdr), SIZEOF_NUT_HEADER) == -1 || svz_sock_write (sock, (char *) nut_put_push (&push), SIZEOF_NUT_PUSH) == -1) { svz_sock_schedule_for_shutdown (sock); svz_free (pushkey); return -1; } /* put push request into hash for later reply detection */ trans = svz_malloc (sizeof (nut_transfer_t)); memcpy (trans, transfer, sizeof (nut_transfer_t)); trans->file = svz_strdup (transfer->file); svz_hash_put (cfg->push, pushkey, trans); svz_free (pushkey);#if ENABLE_DEBUG svz_log (LOG_DEBUG, "nut: sent push request to %s:%u\n", svz_inet_ntoa (sock->remote_addr), ntohs (sock->remote_port));#endif /* put into sent packet hash */ pkt = svz_malloc (sizeof (nut_packet_t)); pkt->sock = sock; pkt->sent = time (NULL); svz_hash_put (cfg->packet, (char *) hdr.id, pkt); } return 0;} /* * Destroy database. */voidnut_destroy_database (nut_config_t *cfg){ nut_file_t *entry; while ((entry = cfg->database) != NULL) { cfg->database = entry->next; svz_free (entry->file); svz_free (entry->path); svz_free (entry); } cfg->db_files = 0; cfg->db_size = 0;}/* * Add a further file to our database. */voidnut_add_database (nut_config_t *cfg, char *path, char *file, off_t size){ nut_file_t *entry; entry = svz_malloc (sizeof (nut_file_t)); entry->file = svz_strdup (file); entry->path = svz_strdup (path); entry->size = size; entry->index = cfg->db_files; entry->next = cfg->database; cfg->database = entry; cfg->db_files++; cfg->db_size += size;}/* * Find a given search pattern within the database. Start to find it * at the given ENTRY. If it is NULL we start at the very beginning. */nut_file_t *nut_find_database (nut_config_t *cfg, nut_file_t *entry, char *search){ if (entry == NULL) entry = cfg->database; else entry = entry->next; while (entry) { if (nut_string_regex (entry->file, search)) return entry; entry = entry->next; } return NULL;}/* * This routine gets a gnutella database entry from a given FILE and * its appropriate INDEX. If no matching file has been found then return * NULL. If FILE is NULL we just search for the the given INDEX. */nut_file_t *nut_get_database (nut_config_t *cfg, char *file, unsigned index){ nut_file_t *entry = NULL; for (entry = cfg->database; entry; entry = entry->next) { if (entry->index == index) if (file == NULL || !strcmp (entry->file, file)) return entry; } return entry;}/* * This routine will re-read the share directory. The routine itself is * recursive. Thus be careful ! It cannot check for recursion loops, yet. */voidnut_read_database_r (nut_config_t *cfg, char *dirname, int depth){ char *path; static struct stat buf; static char filename[NUT_PATH_SIZE];#ifndef __MINGW32__ DIR *dir; static struct dirent *de = NULL;#else HANDLE dir; static WIN32_FIND_DATA de;#endif /* first call */ if (!depth) { nut_destroy_database (cfg); } /* check recursion condition */ if (depth < NUT_PATH_DEPTH) { depth++; /* open the directory */#ifdef __MINGW32__ if (svz_snprintf (filename, NUT_PATH_SIZE - 1, "%s/*", dirname) == -1) return; if ((dir = FindFirstFile (filename, &de)) != INVALID_HANDLE_VALUE)#else if ((dir = opendir (dirname)) != NULL)#endif { /* iterate directory */#ifndef __MINGW32__ while (NULL != (de = readdir (dir)))#else do#endif { if (svz_snprintf (filename, NUT_PATH_SIZE - 1, "%s/%s", dirname, FILENAME) == -1) continue; /* stat the given file */ if (stat (filename, &buf) != -1) { /* add valid files to database */ if (S_ISREG (buf.st_mode) && buf.st_size > 0) { nut_add_database (cfg, dirname, FILENAME, buf.st_size); } /* recurse into directories */ else if (S_ISDIR (buf.st_mode) && FILENAME[0] != '.') { path = svz_strdup (filename); nut_read_database_r (cfg, path, depth); svz_free (path); } } }#ifdef __MINGW32__ while (FindNextFile (dir, &de));#endif closedir (dir); } }}/* * This routine checks for a valid http header for gnutella upload * requests. */intnut_check_upload (svz_socket_t *sock){ char *p = sock->recv_buffer; int len, fill = strlen (NUT_GET); unsigned index = 0; char *end, *file, *f, *hdr; nut_file_t *entry; /* enough receive buffer fill ? */ if (sock->recv_buffer_fill < fill) return 0; /* initial gnutella request detection */ if (memcmp (p, NUT_GET, fill)) return -1; /* parse whole upload header and find the end of the HTTP header */ fill = sock->recv_buffer_fill; while (p < sock->recv_buffer + (fill - 3) && memcmp (p, NUT_SEPERATOR, 4)) p++; if (p < sock->recv_buffer + (fill - 3) && !memcmp (p, NUT_SEPERATOR, 4)) {#if ENABLE_DEBUG svz_log (LOG_DEBUG, "nut: upload header received\n");#endif /* parse first (GET) line */ len = p - sock->recv_buffer + 1; p = sock->recv_buffer + strlen (NUT_GET); end = sock->recv_buffer + len; while (p < end && *p >= '0' && *p <= '9') { index *= 10; index += *p - '0'; p++; } /* parsed file index */ if (p >= end || *p != '/') return -1; f = ++p; while (p < end && *p != '\r' && *p != '\n') p++; /* got actual header property field */ hdr = p + 2; len -= hdr - sock->recv_buffer; while (p > f && memcmp (p, " " NUT_HTTP, strlen (NUT_HTTP) + 1)) p--; if (p <= f) return -1; /* parsed file itself */ fill = p - f; file = svz_malloc (fill + 1); memcpy (file, f, fill); file[fill] = '\0'; /* here we parse all the header properties */ /* find file in database */ if ((entry = nut_get_database (sock->cfg, file, index)) == NULL) {#if ENABLE_DEBUG svz_log (LOG_DEBUG, "nut: no such file: %s, %u\n", file, index);#endif svz_free (file); return -1; } len = end - sock->recv_buffer + 3; svz_sock_reduce_recv (sock, len); svz_free (file); /* disable connection timeout */ sock->idle_func = NULL; sock->userflags |= NUT_FLAG_HDR; if (nut_init_upload (sock, entry) == -1) return -1; } return 0;}/* * If the gnutella header has been successfully parsed this routine * initializes the file upload. */intnut_init_upload (svz_socket_t *sock, nut_file_t *entry){ char *file; nut_config_t *cfg = sock->cfg; struct stat buf; int fd; nut_transfer_t *transfer; /* create filename */ file = svz_malloc (strlen (entry->path) + strlen (entry->file) + 2); sprintf (file, "%s/%s", entry->path, entry->file); /* check file */ if (stat (file, &buf) == -1 || !S_ISREG (buf.st_mode) || buf.st_size <= 0) { svz_log (LOG_ERROR, "nut: invalid file: %s %s\n", file); svz_free (file); return -1; } /* open the file for reading */ if ((fd = open (file, O_RDONLY | O_BINARY)) == -1) { svz_log (LOG_ERROR, "nut: open: %s\n", SYS_ERROR); svz_free (file); return -1; } svz_sock_printf (sock, NUT_GET_OK NUT_AGENT); svz_sock_printf (sock, NUT_CONTENT ": application/binary\r\n"); svz_sock_printf (sock, NUT_LENGTH ": %d\r\n", buf.st_size); svz_sock_printf (sock, "\r\n"); sock->file_desc = fd; sock->read_socket = nut_file_read; sock->write_socket = nut_file_write; sock->disconnected_socket = nut_disconnect_upload; sock->flags |= SOCK_FLAG_FILE; cfg->uploads++; transfer = svz_malloc (sizeof (nut_transfer_t)); memset (transfer, 0, sizeof (nut_transfer_t)); transfer->size = buf.st_size; transfer->original_size = buf.st_size; transfer->file = svz_strdup (file); transfer->start = time (NULL); sock->data = transfer; svz_free (file); return 0;}/* * Disconnection callback for gnutella uploads. */intnut_disconnect_upload (svz_socket_t *sock){ nut_config_t *cfg = sock->cfg; nut_transfer_t *transfer = sock->data; /* decrement amount of concurrent uploads */ cfg->uploads--; /* finally close the received file */ if (close (sock->file_desc) == -1) svz_log (LOG_ERROR, "nut: close: %s\n", SYS_ERROR); /* free the transfer data */ if (transfer) { svz_free (transfer->file); svz_free (transfer); sock->data = NULL; } return 0;}/* * Default gnutella file reader. It is the sock->read_socket callback for * file uploads. */intnut_file_read (svz_socket_t *sock){ int num_read; int do_read; nut_transfer_t *transfer = sock->data; do_read = sock->send_buffer_size - sock->send_buffer_fill; /* * This means the send buffer is currently full, we have to * wait until some data has been send via the socket. */ if (do_read <= 0) { return 0; } /* * Try to read as much data as possible from the file. */ num_read = read (sock->file_desc, sock->send_buffer + sock->send_buffer_fill, do_read); /* Read error occurred. */ if (num_read < 0) { svz_log (LOG_ERROR, "nut: read: %s\n", SYS_ERROR); return -1; } /* Bogus file. File size from stat() was not true. */ if (num_read == 0 && transfer->size != 0) { return -1; } /* Data has been read or EOF reached, set the appropriate flags. */ sock->send_buffer_fill += num_read; transfer->size -= num_read; /* Read all file data ? */ if (transfer->size <= 0) {#if ENABLE_DEBUG svz_log (LOG_DEBUG, "nut: file successfully read\n");#endif /* * no further read()s from the file descriptor, signaling * the writers there will not be additional data from now on */ sock->read_socket = svz_tcp_read_socket; sock->flags &= ~SOCK_FLAG_FILE; } return 0;}/* * This function is the upload callback for the gnutella server. It * throttles its network output to a configured value. */intnut_file_write (svz_socket_t *sock){ int num_written, do_write; nut_transfer_t *transfer = sock->data; nut_config_t *cfg = sock->cfg; time_t t = time (NULL); /* throttle the network output */ num_written = transfer->original_size - transfer->size; if (num_written / (t - transfer->start + 1) > cfg->speed * 1024 / 8) { sock->unavailable = t + RELAX_FD_TIME; return 0; } /* * Write as many bytes as possible, remember how many * were actually sent. */ do_write = (sock->send_buffer_fill > SOCK_MAX_WRITE) ? SOCK_MAX_WRITE : sock->send_buffer_fill; num_written = send (sock->sock_desc, sock->send_buffer, do_write, 0); /* some data has been written */ if (num_written > 0) { sock->last_send = t; if (sock->send_buffer_fill > num_written) { memmove (sock->send_buffer, sock->send_buffer + num_written, sock->send_buffer_fill - num_written); } sock->send_buffer_fill -= num_written; } /* write error occurred */ else if (num_written < 0) { svz_log (LOG_ERROR, "nut: send: %s\n", NET_ERROR); if (svz_errno == SOCK_UNAVAILABLE) { sock->unavailable = t + RELAX_FD_TIME; num_written = 0; } } if (sock->send_buffer_fill == 0 && transfer->size <= 0) {#if ENABLE_DEBUG svz_log (LOG_DEBUG, "nut: file successfully sent\n");#endif return -1; } /* * Return a non-zero value if an error occurred. */ return (num_written < 0) ? -1 : 0;}#else /* ENABLE_GNUTELLA */int nut_transfer_dummy; /* Shut compiler warnings up. */#endif /* not ENABLE_GNUTELLA */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -