void serve_file(dict_epoll_data *ptr, char *uri) { struct stat sbuf; int conn_sock = ptr->sock_fd; int ffd = open(uri, O_RDONLY); #ifdef DEBUG printf("sock_fd: %d, openfile: %s, fd: %d\n", ptr->sock_fd, uri, ffd); #endif if(ffd <= 0) { perror(uri); char *msg = "File not found"; client_error(conn_sock, 404, "Not found", msg); } else { fstat(ffd, &sbuf); if(S_ISREG(sbuf.st_mode)) { ptr->static_fd = ffd; ptr->file_offset = 0; ptr->file_cnt = sbuf.st_size; serve_static(ptr, uri, sbuf.st_size); // will close ffd } else if(S_ISDIR(sbuf.st_mode)) { char *msg = "Dir listing is not impleted"; client_error(conn_sock, 404, "Error", msg); close(ffd); } else { char *msg = "Unknow Error"; client_error(conn_sock, 400, "Error", msg); close(ffd); } } }
/* Establishes two network connections to a Postgres server: one for SQL, and one * for replication. context->conninfo contains the connection string or URL to connect * to, and context->app_name is the client name (which appears, for example, in * pg_stat_activity). Returns 0 on success. */ int client_connect(client_context_t context) { if (!context->conninfo || context->conninfo[0] == '\0') { client_error(context, "conninfo must be set in client context"); return EINVAL; } if (!context->app_name || context->app_name[0] == '\0') { client_error(context, "app_name must be set in client context"); return EINVAL; } context->sql_conn = PQconnectdb(context->conninfo); if (PQstatus(context->sql_conn) != CONNECTION_OK) { client_error(context, "Connection to database failed: %s", PQerrorMessage(context->sql_conn)); return EIO; } /* Parse the connection string into key-value pairs */ char *error = NULL; PQconninfoOption *parsed_opts = PQconninfoParse(context->conninfo, &error); if (!parsed_opts) { client_error(context, "Replication connection info: %s", error); PQfreemem(error); return EIO; } /* Copy the key-value pairs into a new structure with added replication options */ PQconninfoOption *option; int optcount = 2; /* replication, fallback_application_name */ for (option = parsed_opts; option->keyword != NULL; option++) { if (option->val != NULL && option->val[0] != '\0') optcount++; } const char **keys = malloc((optcount + 1) * sizeof(char *)); const char **values = malloc((optcount + 1) * sizeof(char *)); int i = 0; for (option = parsed_opts; option->keyword != NULL; option++) { if (option->val != NULL && option->val[0] != '\0') { keys[i] = option->keyword; values[i] = option->val; i++; } } keys[i] = "replication"; values[i] = "database"; i++; keys[i] = "fallback_application_name"; values[i] = context->app_name; i++; keys[i] = NULL; values[i] = NULL; int err = 0; context->repl.conn = PQconnectdbParams(keys, values, true); if (PQstatus(context->repl.conn) != CONNECTION_OK) { client_error(context, "Replication connection failed: %s", PQerrorMessage(context->repl.conn)); err = EIO; } free(keys); free(values); PQconninfoFree(parsed_opts); return err; }
static void on_connect(void *ud, zn_Tcp *tcp, unsigned err) { if (err != ZN_OK) client_error(tcp); else if (zn_send(tcp, client.send, BLOCK_SIZE, on_client_send, &client) != ZN_OK) client_error(tcp); else if (zn_recv(tcp, client.recv, BLOCK_SIZE, on_client_recv, &client) != ZN_OK) client_error(tcp); }
static void on_client_send(void *ud, zn_Tcp *tcp, unsigned err, unsigned count) { Userdata *data = (Userdata*)ud; if (err != ZN_OK) { ++data->send_err; client_error(tcp); return; } ++data->send_ok; data->send_bytes += count; if (zn_send(tcp, data->send, BLOCK_SIZE, on_client_send, ud) != ZN_OK) client_error(tcp); }
// 处理HTTP事务 void doit(int fd) { int is_static; struct stat sbuf; char buf[LINE_MAX], method[LINE_MAX], uri[LINE_MAX], version[LINE_MAX]; char filename[LINE_MAX], cgiargs[LINE_MAX]; // 读入请求行 readline(fd, buf, sizeof(buf)); sscanf(buf, "%s %s %s", method, uri, version); // 读入请求报头,简单打印到标准输出 while (readline(fd, buf, sizeof(buf))) { printf("%s", buf); if (strcmp(buf, "\r\n") == 0) break; } if (strcmp(method, "GET")) { client_error(fd, method, "501", "Impelementd", "minihttp does not impelement this method"); return; } is_static = parse_uri(uri, filename, cgiargs); if (stat(filename, &sbuf) < 0) { client_error(fd, filename, "404", "Not found", "miniftp couldn't find this file"); return; } if (is_static) /* Serve static content */ { if (!(S_ISREG(sbuf.st_mode)) || !(S_IRUSR & sbuf.st_mode)) { client_error(fd, filename, "403", "Forbidden", "minihttp couldn't read the file"); return; } serve_static(fd, filename, sbuf.st_size); } else /* Serve dynamic content */ { if (!(S_ISREG(sbuf.st_mode)) || !(S_IRUSR & sbuf.st_mode)) { client_error(fd, filename, "403", "Forbidden", "minihttp couldn't run the CGI program"); return; } serve_dynamic(fd, filename, cgiargs); } }
// handle one HTTP request/response transaction void process(int fd, struct sockaddr_in *clientaddr){ printf("Accept Request, fd is %d, pid is %d\n", fd, getpid()); http_request req; memset(&req, 0, sizeof(req)); if (-1 == parse_request(fd, &req)) { //bad socket, don't record printf("Invalid socket!\n"); return; } //update recent visited client information char browser[10][100]; char ip[10][100]; memset(browser, 0, 1000); memset(ip, 0, 1000); strcpy(browser[0], browser_name[req.browser_index]); strcpy(ip[0], inet_ntoa(clientaddr->sin_addr)); update(browser, ip); struct stat sbuf; int status = 200; //server status init as 200 int ffd = open(req.filename, O_RDONLY, 0); if(ffd <= 0){ // detect 404 error and print error log status = 404; client_error(fd, status, "Not Found", "File Not Found"); } else { // get descriptor status fstat(ffd, &sbuf); if(S_ISREG(sbuf.st_mode)){ // server serves static content serve_static(fd, ffd, &req, status); } else if(S_ISDIR(sbuf.st_mode)){ // server handle directory request handle_directory_request(fd, status, browser, ip); } else { // detect 400 error and print error log status = 400; client_error(fd, status, "Bad Request", "Bad Request"); } close(ffd); } // print log/status on the terminal log_access(status, clientaddr, &req); }
/* Reads the next result row from the snapshot query, parses and processes it. * Blocks until a new row is available, if necessary. */ int snapshot_poll(client_context_t context) { int err = 0; PGresult *res = PQgetResult(context->sql_conn); /* null result indicates that there are no more rows */ if (!res) { check(err, exec_sql(context, "COMMIT")); PQfinish(context->sql_conn); context->sql_conn = NULL; // Invoke the commit callback with xid==0 to indicate end of snapshot commit_txn_cb on_commit = context->repl.frame_reader->on_commit_txn; void *cb_context = context->repl.frame_reader->cb_context; if (on_commit) { check(err, on_commit(cb_context, context->repl.start_lsn, 0)); } return 0; } ExecStatusType status = PQresultStatus(res); if (status != PGRES_SINGLE_TUPLE && status != PGRES_TUPLES_OK) { client_error(context, "While reading snapshot: %s: %s", PQresStatus(PQresultStatus(res)), PQresultErrorMessage(res)); PQclear(res); return EIO; } int tuples = PQntuples(res); for (int tuple = 0; tuple < tuples; tuple++) { check(err, snapshot_tuple(context, res, tuple)); } PQclear(res); return err; }
/** * Mark clients as having sent status as specified by PRSTATUS */ void set_proxy_status(struct finfo_t *finfo, const unsigned char *data, int hostidx, int nak_count) { struct prstatus_h *prstatus; uint32_t *addrlist; int clientcnt, clientidx, dupmsg, i; prstatus = (struct prstatus_h *)data; addrlist = (uint32_t *)(data + sizeof(struct prstatus_h)); clientcnt = ntohs(prstatus->destcount); for (i = 0; i < clientcnt; i++) { clientidx = find_client(addrlist[i]); if (clientidx == -1) { log1(0, 0, "Client %s via proxy %s not found", inet_ntoa(to_addr(addrlist[i])), destlist[hostidx].name); } else if (destlist[clientidx].proxyidx != hostidx) { log1(0, 0, "Client %s found via proxy %s, expected proxy %s", destlist[clientidx].name, destlist[destlist[clientidx].proxyidx].name, destlist[hostidx].name); } else if (!client_error(clientidx)) { dupmsg = (destlist[clientidx].status == DEST_ACTIVE); destlist[clientidx].status = DEST_ACTIVE; log(0, 0, " For client%s %s", dupmsg ? "+" : "", destlist[clientidx].name); if (!dupmsg) { finfo->deststate[clientidx].naks += nak_count; } } } }
static void on_connect(void *ud, zn_Tcp *tcp, unsigned err) { zn_BufferPoolNode *data = (zn_BufferPoolNode*)ud; if (err != ZN_OK) { client_error(tcp, ud); return; } if (zn_recv(tcp, zn_recvbuff(&data->recv), zn_recvsize(&data->recv), on_client_recv, ud) != ZN_OK) client_error(tcp, ud); else if (zn_sendprepare(&data->send, send_data, BLOCK_SIZE) && zn_send(tcp, zn_sendbuff(&data->send), zn_sendsize(&data->send), on_client_send, ud) != ZN_OK) client_error(tcp, ud); }
static void on_client_packet(void *ud, const char *buff, size_t len) { zn_BufferPoolNode *data = (zn_BufferPoolNode*)ud; if (zn_sendprepare(&data->send, buff, len) && zn_send(data->tcp, zn_sendbuff(&data->send), zn_sendsize(&data->send), on_client_send, ud) != ZN_OK) client_error(data->tcp, ud); }
int put_client(int sock, char *cmd) { char **tab; int fd; tab = ft_strsplit(cmd, ' '); if (!tab[1]) return (client_error(-2)); fd = open(tab[1], O_RDONLY); if (fd == -1) return (client_error(-3)); send(sock, cmd, ft_strlen(cmd), 0); file_to_sock(sock, fd); printf("SUCCESS: file %s has been sent to server\n", tab[1]); ft_tabfree(&tab); return (0); }
void *xmalloc(size_t size) { void *ptr; if ((ptr = malloc(size)) == NULL) client_error("malloc failed"); return (ptr); }
void client_send(Client *client, char *m) { int n; char buff[BUFFER_SIZE]; Message msg; msg.type = MSG_TEXT; TextMessage payload; strncpy(payload.content, m, strlen(m)); msg.text = payload; n = write(client->socket, &msg, sizeof(msg)); if (n < 0) client_error("writing to socket"); bzero(buff, BUFFER_SIZE); n = read(client->socket, buff, BUFFER_SIZE); if (n < 0) client_error("reading to socket"); client->handler(buff); }
/* Executes a SQL command that returns no results. */ int exec_sql(client_context_t context, char *query) { PGresult *res = PQexec(context->sql_conn, query); if (PQresultStatus(res) == PGRES_COMMAND_OK) { PQclear(res); return 0; } else { client_error(context, "Query failed: %s: %s", query, PQerrorMessage(context->sql_conn)); PQclear(res); return EIO; } }
/* Blocks until more data is received from the server. You don't have to use * this if you have your own select loop. */ int db_client_wait(client_context_t context) { fd_set input_mask; FD_ZERO(&input_mask); int rep_fd = PQsocket(context->repl.conn); int max_fd = rep_fd; FD_SET(rep_fd, &input_mask); if (context->sql_conn) { int sql_fd = PQsocket(context->sql_conn); if (sql_fd > max_fd) max_fd = sql_fd; FD_SET(sql_fd, &input_mask); } struct timeval timeout; timeout.tv_sec = 1; timeout.tv_usec = 0; int ret = select(max_fd + 1, &input_mask, NULL, NULL, &timeout); if (ret == 0 || (ret < 0 && errno == EINTR)) { return 0; /* timeout or signal */ } if (ret < 0) { client_error(context, "select() failed: %s", strerror(errno)); return errno; } /* Data has arrived on the socket */ if (!PQconsumeInput(context->repl.conn)) { client_error(context, "Could not receive replication data: %s", PQerrorMessage(context->repl.conn)); return EIO; } if (context->sql_conn && !PQconsumeInput(context->sql_conn)) { client_error(context, "Could not receive snapshot data: %s", PQerrorMessage(context->sql_conn)); return EIO; } return 0; }
/* Initiates the non-blocking capture of a consistent snapshot of the database, * using the exported snapshot context->repl.snapshot_name. */ int snapshot_start(client_context_t context) { if (!context->repl.snapshot_name || context->repl.snapshot_name[0] == '\0') { client_error(context, "snapshot_name must be set in client context"); return EINVAL; } int err = 0; check(err, exec_sql(context, "BEGIN")); check(err, exec_sql(context, "SET TRANSACTION ISOLATION LEVEL REPEATABLE READ")); PQExpBuffer query = createPQExpBuffer(); appendPQExpBuffer(query, "SET TRANSACTION SNAPSHOT '%s'", context->repl.snapshot_name); check(err, exec_sql(context, query->data)); destroyPQExpBuffer(query); Oid argtypes[] = { 25, 16 }; // 25 == TEXTOID, 16 == BOOLOID const char *args[] = { "%", context->allow_unkeyed ? "t" : "f" }; if (!PQsendQueryParams(context->sql_conn, "SELECT bottledwater_export(table_pattern := $1, allow_unkeyed := $2)", 2, argtypes, args, NULL, NULL, 1)) { // The final 1 requests results in binary format client_error(context, "Could not dispatch snapshot fetch: %s", PQerrorMessage(context->sql_conn)); return EIO; } if (!PQsetSingleRowMode(context->sql_conn)) { client_error(context, "Could not activate single-row mode"); return EIO; } // Invoke the begin-transaction callback with xid==0 to indicate start of snapshot begin_txn_cb begin_txn = context->repl.frame_reader->on_begin_txn; void *cb_context = context->repl.frame_reader->cb_context; if (begin_txn) { check(err, begin_txn(cb_context, context->repl.start_lsn, 0)); } return 0; }
/* Processes one tuple of the snapshot query result set. */ int snapshot_tuple(client_context_t context, PGresult *res, int row_number) { if (PQnfields(res) != 1) { client_error(context, "Unexpected response with %d fields", PQnfields(res)); return EIO; } if (PQgetisnull(res, row_number, 0)) { client_error(context, "Unexpected null response value"); return EIO; } if (PQfformat(res, 0) != 1) { /* format 1 == binary */ client_error(context, "Unexpected response format: %d", PQfformat(res, 0)); return EIO; } /* wal_pos == 0 == InvalidXLogRecPtr */ int err = parse_frame(context->repl.frame_reader, 0, PQgetvalue(res, row_number, 0), PQgetlength(res, row_number, 0)); if (err) { client_error(context, "Error parsing frame data: %s", avro_strerror()); } return err; }
static void on_client_send(void *ud, zn_Tcp *tcp, unsigned err, unsigned count) { zn_BufferPoolNode *data = (zn_BufferPoolNode*)ud; if (err != ZN_OK) { ++send_err; client_error(tcp, data); return; } ++send_ok; send_bytes += count; if (zn_sendfinish(&data->send, count)) zn_send(tcp, zn_sendbuff(&data->send), zn_sendsize(&data->send), on_client_send, ud); }
void do_request(http::request<Body> const& req, beast::error_code& ec) { // verb must be get if(req.method() != http::verb::get) { http::write(sock_, client_error(http::status::bad_request, "Unsupported method"), ec); return; } // Request path must be absolute and not contain "..". if( req.target().empty() || req.target()[0] != '/' || req.target().find("..") != std::string::npos) { http::write(sock_, client_error(http::status::not_found, "File not found"), ec); return; } auto full_path = root_.to_string(); full_path.append(req.target().data(), req.target().size()); beast::error_code file_ec; auto res = get(full_path, file_ec); if(file_ec == beast::errc::no_such_file_or_directory) { http::write(sock_, not_found(), ec); } else if(ec) { http::write(sock_, server_error(file_ec), ec); } else { http::serializer<false, decltype(res)::body_type> sr{res}; http::write(sock_, sr, ec); } }
int main(int ac, char **av) { int port; int sock; if (ac != 3) return (client_error(-1)); port = ft_atoi(av[2]); sock = create_client(av[1], port); if (sock == -1) return (-1); make_client(sock); return (0); }
static void on_client_recv(void *ud, zn_Tcp *tcp, unsigned err, unsigned count) { zn_BufferPoolNode *data = (zn_BufferPoolNode*)ud; if (err != ZN_OK) { ++recv_err; client_error(tcp, data); return; } ++recv_ok; recv_bytes += count; zn_recvfinish(&data->recv, count); zn_recv(tcp, zn_recvbuff(&data->recv), zn_recvsize(&data->recv), on_client_recv, ud); }
/* Sets *exists to true if a replication slot with the name context->repl.slot_name * already exists, and false if not. In addition, if the slot already exists, * context->repl.start_lsn is filled in with the LSN at which the client should * restart streaming. */ int replication_slot_exists(client_context_t context, bool *exists) { if (!context->repl.slot_name || context->repl.slot_name[0] == '\0') { client_error(context, "repl.slot_name must be set in client context"); return EINVAL; } int err = 0; Oid argtypes[] = { 19 }; // 19 == NAMEOID const char *args[] = { context->repl.slot_name }; PGresult *res = PQexecParams(context->sql_conn, "SELECT restart_lsn FROM pg_replication_slots where slot_name = $1", 1, argtypes, args, NULL, NULL, 0); if (PQresultStatus(res) != PGRES_TUPLES_OK) { client_error(context, "Could not check for existing replication slot: %s", PQerrorMessage(context->sql_conn)); err = EIO; goto done; } *exists = (PQntuples(res) > 0 && !PQgetisnull(res, 0, 0)); if (*exists) { uint32 h32, l32; if (sscanf(PQgetvalue(res, 0, 0), "%X/%X", &h32, &l32) != 2) { client_error(context, "Could not parse restart LSN: \"%s\"", PQgetvalue(res, 0, 0)); err = EIO; goto done; } else { context->repl.start_lsn = ((uint64) h32) << 32 | l32; } } done: PQclear(res); return err; }
int get_client(int sock, char *cmd) { char **tab; int ret; char buf[BUFF_LEN + 1]; tab = ft_strsplit(cmd, ' '); if (!tab[1]) return (client_error(-4)); send(sock, cmd, ft_strlen(cmd), 0); ret = recv(sock, buf, BUFF_LEN, 0); buf[ret] = '\0'; if (!ft_strcmp(GET_FAIL, buf)) { printf("ERROR: You failed to receive the file: %s\n", tab[1]); return (0); } else if (!ft_strcmp(GET_OK, buf)) ft_get_file(tab[1], sock); ft_tabfree(&tab); return (0); }
/** * Perform the Announce/Register phase for a particular group/file * Group & encryption: ->ANNOUNCE <-REGISTER ->KEYINFO <-INFO_ACK * Group & no encryption: ->ANNOUNCE <-REGISTER ->REG_CONF * Files within a group: ->FILEINFO <-INFO_ACK * If client_key == 1, REGISTER is followed by CLIENT_KEY * Returns 1 if at least one client responded, 0 if none responded */ int announce_phase(struct finfo_t *finfo) { time_t endtime; int attempt, resend, announce, regconf, keyinfo, fileinfo, open, anyerror; int len, rval, rcv_status, last_pass, gotall, gotone, allreg, regdone, i; unsigned char *packet, *decrypted; struct uftp_h *header; struct timeval timeout; struct sockaddr_in receiver; if (finfo->file_id) { log1(0, 0, "File ID: %04X Name: %s", finfo->file_id, finfo->filename); log1(0, 0, " sending as: %s", finfo->destfname); switch (finfo->ftype) { case FTYPE_REG: log(0, 0, "Bytes: %s Blocks: %d Sections: %d", printll(finfo->size), finfo->blocks, finfo->sections); break; case FTYPE_DIR: log(0, 0, "Empty directory"); break; case FTYPE_LINK: log(0, 0, "Symbolic link to %s", finfo->linkname); break; } } else { log(0, 0, "Initializing group"); if (sync_mode) { log0(0, 0, "- Connect -"); } } rval = 1; packet = calloc(mtu, 1); decrypted = calloc(mtu, 1); if ((packet == NULL) || (decrypted == NULL)) { syserror(0, 0, "calloc failed!"); exit(1); } header = (struct uftp_h *)packet; endtime = time(NULL) + announce_time; announce = (finfo->file_id == 0); regconf = (announce && (keytype == KEY_NONE)); keyinfo = (announce && (keytype != KEY_NONE)); fileinfo = (finfo->file_id != 0); open = (destcount == 0); for (i = 0; i < destcount; i++) { // At start of group, initialize all clients/proxies to DEST_MUTE. // At start of file, initialize proxies to DEST_ACTIVE (since they // don't respond directly to a FILEINFO) and clients to DEST_REGISTERED. if (announce) { destlist[i].status = DEST_MUTE; } else if (!client_error(i)) { if (destlist[i].clientcnt != -1) { destlist[i].status = DEST_ACTIVE; } else { destlist[i].status = DEST_REGISTERED; } } } timeout.tv_sec = announce_int / 1000; timeout.tv_usec = (announce_int % 1000) * 1000; resend = 1; attempt = 1; last_pass = 0; regdone = 0; while (time(NULL) < endtime) { // On the initial pass, or when the announce timeout trips, // send any necessary messages. if (resend) { if (keyinfo && !send_keyinfo(finfo, attempt)) { continue; } if (announce && !send_regconf(finfo, attempt, regconf)) { continue; } if (fileinfo && !send_fileinfo(finfo, attempt)) { continue; } if (announce && !send_announce(finfo, attempt, open)) { continue; } resend = 0; } // TODO: Currently, the interval between sends is really an inactivity // timer, not the actual time between sends. We might want to change // it to that, and perhaps add an extra "overage" timer in case we're // still processing responses when we're due to resend, that way we'll // always wait some minimum amount of time. if ((rcv_status = read_packet(sock, &receiver, packet, &len, mtu, &timeout)) == -1) { continue; } else if (rcv_status == 0) { attempt++; resend = 1; if (last_pass) break; continue; } if (!validate_packet(packet, len, finfo)) { continue; } if (!handle_announce_phase(packet, decrypted, &receiver, finfo, announce, open, regconf)) { continue; } if (!open) { for (i = 0, gotall = 1, allreg = 1; (i < destcount) && (gotall || allreg); i++) { if (announce) { gotall = gotall && ((destlist[i].status == DEST_ACTIVE) || (destlist[i].status == DEST_ABORT)); allreg = allreg && ((destlist[i].status == DEST_ACTIVE) || (destlist[i].status == DEST_REGISTERED) || (destlist[i].status == DEST_ABORT)); } else { gotall = gotall && ((destlist[i].status == DEST_ACTIVE) || (destlist[i].status == DEST_DONE) || (client_error(i))); } } if (gotall) { // Break out right away if this is a file registration. // For group registration, do one last wait, even if // encryption is enabled since we can still send a // REG_CONF for a client behind a proxy. // Change the wait interval to the client's register_int * 1.5 // to allow for late registers. // Be careful not to overrun the phase timeout! if (finfo->file_id != 0) break; timeout.tv_sec = (int)(register_int / 1000 * 1.5); timeout.tv_usec = (int)((register_int % 1000) * 1000 * 1.5); if (timeout.tv_sec > endtime) { #ifdef WINDOWS timeout.tv_sec = (long)endtime; #else timeout.tv_sec = endtime; #endif timeout.tv_usec = 0; } if (!last_pass) { log(0, 0, "Late registers:"); } last_pass = 1; send_regconf(finfo, attempt + 1, regconf); } else if (announce && allreg && !regdone) { // All have registered, so don't wait to send the next message resend = 1; regdone = 1; } } } for (i = 0, gotone = 0, anyerror = 0; i < destcount; i++) { gotone = gotone || (((destlist[i].status == DEST_ACTIVE) || (destlist[i].status == DEST_DONE)) && (destlist[i].clientcnt == -1)); if (destlist[i].status == DEST_REGISTERED) { log1(0, 0, "Couldn't get INFO_ACK from %s", destlist[i].name); destlist[i].status = DEST_LOST; anyerror = 1; } if ((destlist[i].status == DEST_MUTE) || (destlist[i].status == DEST_ABORT)) { anyerror = 1; } } if (anyerror && quit_on_error) { log0(0, 0, "Aboring all clients"); send_abort(finfo, "A client dropped out, aborting all", &receive_dest, NULL, (keytype != KEY_NONE), 0); for (i = 0; i < destcount; i++) { if (destlist[i].status == DEST_ACTIVE) { destlist[i].status = DEST_ABORT; } } rval = 0; } if (!gotone) { log0(0, 0, "Announce timed out"); rval = 0; } if (open) { send_regconf(finfo, attempt, regconf); } if ((finfo->file_id == 0) && sync_mode) { for (i = 0; i < destcount; i++) { if (destlist[i].status == DEST_ACTIVE) { log0(0, 0, "CONNECT;success;%s", destlist[i].name); } else { log0(0, 0, "CONNECT;failed;%s", destlist[i].name); } } log0(0, 0, "- Transfer -"); } free(packet); free(decrypted); return rval; }
/** * Request NAKs from all clients. * Returns the aggregate number of NAKs for the section. */ int get_naks(struct finfo_t *finfo, unsigned int pass, unsigned int section, int *alldone) { unsigned char *packet, *decrypted; struct uftp_h *header; struct timeval timeout; struct sockaddr_in receiver; int resend, attempt, last_pass, gotall, anyerror; int blocks_this_sec, section_offset; int rcv_status, len, nakidx, numnaks, i, j; time_t endtime; packet = calloc(mtu, 1); decrypted = calloc(mtu, 1); if ((packet == NULL) || (decrypted == NULL)) { syserror(0, 0, "calloc failed!"); exit(1); } header = (struct uftp_h *)packet; section_offset = (blocksize * 8) * (section - 1); blocks_this_sec = ((section < finfo->sections) ? (blocksize * 8) : (finfo->blocks % (blocksize * 8))); if (finfo->sections && !blocks_this_sec) blocks_this_sec = blocksize * 8; for (i = 0; i < blocks_this_sec; i++) { nakidx = i + section_offset; finfo->naklist[nakidx] = 0; } for (i = 0; i < destcount; i++) { if (client_error(i)) { continue; } if (destlist[i].clientcnt != -1) { destlist[i].status = DEST_ACTIVE; } else if (destlist[i].status == DEST_ACTIVE) { destlist[i].status = DEST_STATUS; } free(destlist[i].last_status); destlist[i].last_status = NULL; for (j = 0; j < destlist[i].last_prstatus_cnt; j++) { free(destlist[i].last_prstatus[j]); destlist[i].last_prstatus[j] = NULL; } destlist[i].last_prstatus_cnt = 0; } endtime = time(NULL) + status_time; timeout.tv_sec = status_int / 1000; timeout.tv_usec = (status_int % 1000) * 1000; resend = 1; attempt = 1; gotall = 0; last_pass = 0; while ((time(NULL) < endtime) && (!gotall)) { if (resend) { if (!send_doneconf(finfo, attempt)) { continue; } if (!send_done(finfo, attempt, pass, section)) { continue; } resend = 0; } // See comments in announce_phase regarding timing if ((rcv_status = read_packet(sock, &receiver, packet, &len, mtu, &timeout)) == -1) { continue; } else if (rcv_status == 0) { attempt++; resend = 1; if (last_pass) break; continue; } if (!validate_packet(packet, len, finfo)) { continue; } if (!handle_transfer_phase(packet, decrypted, &receiver, blocks_this_sec, section_offset, pass, section, finfo)) { continue; } for (i = 0, gotall = 1, *alldone = 1; (i < destcount) && (gotall || *alldone); i++) { gotall = gotall && (destlist[i].status != DEST_STATUS); *alldone = *alldone && ((destlist[i].status == DEST_DONE) || (client_error(i)) || (destlist[i].clientcnt != -1)); } if (*alldone) { if (finfo->file_id != 0) break; // Change the wait interval to the client's done_int * 1.5 // to allow for late completions timeout.tv_sec = (int)(done_int / 1000 * 1.5); timeout.tv_usec = (int)((done_int % 1000) * 1000 * 1.5); if (!last_pass) { log(0, 0, "Late completions:"); } last_pass = 1; gotall = 0; send_doneconf(finfo, attempt + 1); } } anyerror = 0; if (*alldone) { send_doneconf(finfo, attempt + 1); } else if (!gotall) { for (i = 0, *alldone = 1; i < destcount; i++) { if (destlist[i].status == DEST_STATUS) { log1(0, 0, "Couldn't get STATUS from %s", destlist[i].name); destlist[i].status = DEST_LOST; anyerror = 1; } *alldone = *alldone && ((destlist[i].status == DEST_DONE) || (client_error(i)) || (destlist[i].clientcnt != -1)); } } if (anyerror && quit_on_error) { log0(0, 0, "Aboring all clients"); send_abort(finfo, "A client dropped out, aborting all", &receive_dest, NULL, (keytype != KEY_NONE), 0); for (i = 0; i < destcount; i++) { if (destlist[i].status == DEST_ACTIVE) { destlist[i].status = DEST_ABORT; } } *alldone = 1; } for (i = 0, numnaks = 0; i < blocks_this_sec; i++) { nakidx = i + section_offset; if (finfo->naklist[nakidx]) { numnaks++; } } free(packet); free(decrypted); return numnaks; }
// 处理HTTP事务 void* doit(void *arg) { int fd = *((int *)arg); int is_static; struct stat sbuf; char buf[LINE_MAX] = {0}, method[LINE_MAX] = {0}, uri[LINE_MAX] = {0}, version[LINE_MAX] = {0}; char filename[LINE_MAX] = {0}, cgiargs[LINE_MAX] = {0}; do { // 读入请求行 if (readline(fd, buf, sizeof(buf)) > 0) { if (sscanf(buf, "%s %s %s", method, uri, version) != 3) { client_error(fd, "GET", "501", "Not Impelemented", "error request line"); break; } } // 读入请求报头,简单打印到标准输出 while (readline(fd, buf, sizeof(buf)) > 0) { if (strcmp(buf, "\r\n") == 0) break; } /* 过滤所有非GET请求*/ if (strcmp(method, "GET")) { client_error(fd, method, "501", "Not Impelementd", "minihttp does not impelement this method"); break; } is_static = parse_uri(uri, filename, cgiargs); if (stat(filename, &sbuf) < 0) { client_error(fd, filename, "404", "Not found", "miniftp couldn't find this file"); break; } if (is_static) /* Serve static content */ { if (!(S_ISREG(sbuf.st_mode)) || !(S_IRUSR & sbuf.st_mode)) { client_error(fd, filename, "403", "Forbidden", "minihttp couldn't read the file"); break; } serve_static(fd, filename, sbuf.st_size); } else /* Serve dynamic content */ { if (!(S_ISREG(sbuf.st_mode)) || !(S_IRUSR & sbuf.st_mode)) { client_error(fd, filename, "403", "Forbidden", "minihttp couldn't run the CGI program"); break; } serve_dynamic(fd, filename, cgiargs); } } while (0); close(fd); free((int *)arg); return NULL; }
/** * Save the state of a failed transfer so it can restarted later. */ void write_restart_file(uint32_t group_id) { struct server_restart_t header; char restart_name[MAXFILENAME]; char proxy_listed[MAXPROXYDEST]; int fd, opened, i, j, proxycnt, found; memset(proxy_listed, 0, sizeof(proxy_listed)); opened = 0; proxycnt = 0; for (i = 0; i < destcount; i++) { if ((destlist[i].clientcnt == -1) && client_error(i)) { if (!opened) { snprintf(restart_name, sizeof(restart_name), "_group_%08X_restart", group_id); if ((fd = open(restart_name, OPENWRITE | O_CREAT | O_TRUNC, 0644)) == -1) { syserror(0, 0, "Failed to create restart file"); return; } // Write header header.group_id = group_id; header.filecount = filecount; if (file_write(fd, &header, sizeof(header)) == -1) { log(0, 0, "Failed to write header for restart file"); goto errexit; } // Write file list for (j = 0; j < filecount; j++) { if (file_write(fd, filelist[j],sizeof(filelist[j])) == -1) { log(0, 0, "Failed to write filename for restart file"); goto errexit; } } opened = 1; } if (!write_restart_host(fd, i)) { goto errexit; } if (destlist[i].proxyidx != -1) { for (j = 0, found = 0; (j < proxycnt) && !found; j++) { if (proxy_listed[j] == destlist[i].proxyidx) { found = 1; } } if (!found) { if (!write_restart_host(fd, destlist[i].proxyidx)) { goto errexit; } proxy_listed[proxycnt++] = destlist[i].proxyidx; } } } } if (opened) { close(fd); } return; errexit: close(fd); unlink(restart_name); }
/** * Performs the Transfer phase for a particular file * Returns 1 if at least one client finished, 0 if all are dropped or aborted */ int transfer_phase(struct finfo_t *finfo) { unsigned char *packet, *encpacket, *data; char path[MAXPATHNAME]; struct uftp_h *header; struct fileseg_h *fileseg; int max_time, alldone, numbytes, sent_blocks, current_naks; unsigned int pass, section, numnaks, block; struct timeval start_time, last_sent, current_sent; int64_t avgwait, waitcnt, overage, tdiff; f_offset_t offset, curr_offset; int file, i; if (finfo->file_id != 0) { // First check to see if all clients are already done for this file. // This can happen on a restart when the file finished on the // last attempt and responded to the FILEINFO with a COMPLETE for (i = 0, alldone = 1; (i < destcount) && alldone; i++) { alldone = alldone && ((destlist[i].status == DEST_DONE) || (client_error(i)) || (destlist[i].clientcnt != -1)); } if (alldone) { gettimeofday(&start_time, NULL); print_status(finfo, start_time); return 1; } } // If rate is -1, use 100Mbps for purpose of calculating max time max_time = (int)floor(((double)weight / 100) * ((double)finfo->size / (((rate == -1) ? 100000 : rate) / 8) / 1024)); if (max_time < min_time) { max_time = min_time; } if ((finfo->file_id != 0) && (finfo->ftype == FTYPE_REG)) { log(0, 0, "Maximum file transfer time: %d seconds", max_time); snprintf(path, sizeof(path), "%s%c%s", finfo->basedir, PATH_SEP, finfo->filename); if ((file = open(path, OPENREAD, 0)) == -1) { syserror(0, 0, "Error opening file"); return 1; } } else { // At end of group, all non-errored client are DEST_DONE from the // last file, so reset them to DEST_ACTIVE to get the final COMPLETE. for (i = 0; i < destcount; i++) { if (!client_error(i)) { destlist[i].status = DEST_ACTIVE; } } } packet = calloc(mtu, 1); encpacket = calloc(mtu, 1); if ((packet == NULL) || (encpacket == NULL)) { syserror(0, 0, "calloc failed!"); exit(1); } header = (struct uftp_h *)packet; fileseg = (struct fileseg_h *)(packet + sizeof(struct uftp_h)); data = (unsigned char *)fileseg + sizeof(struct fileseg_h); set_uftp_header(header, FILESEG, finfo, &receive_dest); pass = 1; alldone = 0; gettimeofday(&start_time, NULL); do { avgwait = 0; waitcnt = 0; numnaks = 0; overage = 0; section = 1; curr_offset = 0; sent_blocks = 0; gettimeofday(&last_sent, NULL); if (finfo->file_id != 0) { log(0, 0, "Sending file...pass %d", pass); lseek_func(file, 0, SEEK_SET); } else { log(0, 0, "Finishing group"); } fileseg->func = FILESEG; fileseg->file_id = htons(finfo->file_id); fileseg->pass = pass; fileseg->section = htons(section); for (block = 0; block < finfo->blocks; block++) { // If all clients received this file partially on a prior attempt // and it's the first pass, request NAKs for all sections // right away and don't send any data packets. if (((pass == 1) || finfo->naklist[block]) && !((pass == 1) && finfo->partial)) { if (diff_sec(last_sent, start_time) > max_time) { log0(0, 0, "Max file transfer time exceeded"); send_abort(finfo, "Max file transfer time exceeded", &receive_dest, NULL, (keytype != KEY_NONE), !quit_on_error); alldone = 1; for (i = 0; i < destcount; i++) { if (quit_on_error || ((destlist[i].status == DEST_ACTIVE) && destlist[i].clientcnt == -1 )) { destlist[i].status = DEST_ABORT; } } break; } // On the first pass, go straight through the file. // On later passes, seek to the next packet. if (pass != 1) { log4(0, 0, "Resending %d", block); if (!seek_block(file, block, &offset, curr_offset)) { continue; } } if ((numbytes = read(file, data, blocksize)) == -1) { syserror(0, 0, "read failed"); continue; } if (pass != 1) { curr_offset = offset + numbytes; } // Keep track of how long we really slept compared to how // long we expected to sleep. If we went over, subtract the // time over from the next sleep time. This way we maintain // the proper average sleep time. This can result in multiple // packets going out at once, potentially losing packets. if (packet_wait > overage) { usleep(packet_wait - (int32_t)overage); } gettimeofday(¤t_sent, NULL); tdiff = diff_usec(current_sent, last_sent); avgwait += tdiff; waitcnt++; if (packet_wait) overage += tdiff - packet_wait; last_sent = current_sent; fileseg->seq_num = htonl(block); send_data(finfo, packet, numbytes, encpacket); sent_blocks++; } if ((block % (blocksize * 8) == (blocksize * 8) - 1) || (block == finfo->blocks - 1)) { if ((pass == 1) || sent_blocks) { current_naks = get_naks(finfo, pass, section, &alldone); numnaks += current_naks; if ((rate != -1) && (cc_count > 0)) { if (!read_cc_config(cc_config)) { log1(0, 0, "Error rereading congestion control " "config, using prior values"); } adjust_rate(current_naks, sent_blocks); } overage = 0; if (alldone) break; } sent_blocks = 0; gettimeofday(&last_sent, NULL); fileseg->section = htons(++section); } } if ((finfo->size == 0) && !alldone) { // If it's the end of the group, or an empty file, a DONE was // never sent, so send it now numnaks += get_naks(finfo, pass, section, &alldone); } if (finfo->file_id != 0) { log(0, 0, "Average wait time = %.2f us", (waitcnt == 0) ? 0 : (float)avgwait / waitcnt); log(0, 0, "Received %d distinct NAKs for pass %d", numnaks, pass); } pass++; } while (!alldone); if ((finfo->file_id != 0) && (finfo->ftype == FTYPE_REG)) { close(file); } print_status(finfo, start_time); free(packet); free(encpacket); for (i = 0; i < destcount; i++) { if (quit_on_error) { // Check to see that all finished if ((destlist[i].status != DEST_DONE) && (destlist[i].clientcnt == -1)) { return 0; } } else { // Check to see if at least one finished if (destlist[i].status == DEST_DONE) { return 1; } } } if (quit_on_error) { return 1; } else { return 0; } }