/****************************************************************************** Start obsluhy klienta Inspirace (vytvoreni socketu, bind): http://beej.us/guide/bgnet/output/html/multipage/clientserver.html#datagram *******************************************************************************/ int Client::start(Opt *options, struct sockaddr_storage client_addr, int index, char *buf, int numbytes, int mtu) { // Generovani nahodneho cisla pro port, na kterem bude klient obsluhovan srand (getpid()); int port = rand() % MAX_PORT + MIN_PORT; client_port = to_string(port); service_ip = options->address_at(index); int sockfd; struct addrinfo hints, *servinfo, *p; int rv; socklen_t addr_len; char s[INET6_ADDRSTRLEN]; memset(&hints, 0, sizeof hints); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_DGRAM; signal(SIGINT, client_signal_reaction); // Registrace funkce pro odchyt signalu if ((rv = getaddrinfo(service_ip.c_str(), client_port.c_str(), &hints, &servinfo)) != 0) { cerr << "Error - Service " << service_ip << ", " << client_port << gai_strerror(rv) << endl; return EXIT_FAILURE; } for(p = servinfo; p != NULL; p = p->ai_next) { if ((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) { continue; } if (bind(sockfd, p->ai_addr, p->ai_addrlen) == -1) { close(sockfd); continue; } break; } if (p == NULL) { cerr << "Error - Service " << service_ip << ", " << client_port << " - failed to bind socket" << endl; return EXIT_FAILURE; } freeaddrinfo(servinfo); client_socket = sockfd; // Ulozim si deskr. socketu // Nastavi se vychozi timeout socketu if((set_socket_timeout(options, IMPLICIT_TIMEOUT)) == SET_FAILED) return EXIT_FAILURE; // ulozim si klientovu IP pro potreby vypisu informacnich hlasek string client_ip = inet_ntop(client_addr.ss_family, get_in_addr((struct sockaddr *)&client_addr), s, sizeof s); mtu_size = mtu; // Ulozim si MTU // Cinnost obsluhy serveru string err_msg; unsigned int data_counter = 0; // Pocitadlo dat unsigned int block_counter = 0; // Pocitadlo bloku int data_len; // Pocet odeslanych B int mode; // Mod prenosu int tryouts = 0; // Pocet pokusu o znovu navazani komunikace bool reading_data = false; // Flag cteni dat bool writing_data = false; // Flag zapisu dat int ack; // Cislo potvrzeneho bloku int type; // Typ paketu int file_status; // Info o uspesnosti operace se souborem int bytes_write; // Pocet zapsanych bajtu int block_num; // Cislo bloku int recv_status; // Pocet prijatych B pri volani funkce recvfrom() char *file_buf; // Buffer pro nacitani dat ze souboru int file_buf_size;// Velikost bufferu File *f = new File(); // Nova instance objektu pro praci se soubory while(1) {// Smycka obsluhy klienta if(writing_data == true) type = packet_type(file_buf); // buffer na heapu else type = packet_type(buf); // buffer na zasobniku switch(type) {// Podle typu paketu se provadi dana operace case RRQ: {// Zadost o cteni get_filename(buf); // ukladani jmena souboru // Zjisti se mod prenosu mode = transfer_mode(buf); if(mode == UNKNOWN_MODE) {// neznamy mod err_msg = "Unknown mode"; send_error(sockfd, client_addr, err_msg, ERROR_IL); cout << current_time() << client_ip << " Unknown mode, operation aborted" << endl; clean_sources(f, sockfd); return 1; } else if(mode == NETASCII) { cout << current_time() + client_ip + " Requested READ of " << filename << " [netascii]"<< endl; } else if(mode == OCTET) { cout << current_time() + client_ip + " Requested READ of " << filename << " [octet]"<< endl; } // Otevre se soubor pro cteni file_status = f->open(filename, options->working_path(), FILE_IN, mode_tr); if(file_status == NOT_FOUND) {// Soubor nebyl nalezen err_msg = "File not found"; send_error(sockfd, client_addr, err_msg, ERROR_NF); cout << current_time() + client_ip + " File " << filename << " not found, operation aborted" << endl; clean_sources(f, sockfd); return 1; } else if(file_status == PERM_DEN) {// Nedostatecne opravneni err_msg = "Permission denided"; send_error(sockfd, client_addr, err_msg, ERROR_AV); cout << current_time() << client_ip << " Permission denided, operation aborted" << endl; clean_sources(f, sockfd); return EXIT_FAILURE; } // Zjisti se pritomnost rozsireni if(opt_extension(buf, numbytes, options, f) == EXTENSION) {// request obsahuje options send_oack(sockfd, client_addr); reading_data = true; // Alokace bufferu pro nacitani souboru if(block_size.used == true) {// byla specifikovana velikost bloku file_buf = new char[block_size.value + 1]; file_buf_size = block_size.value + 1; if(f->get_file_size() > MAX_BLOCK_COUNT * block_size.value) {// Soubor je prilis velky na prenos pres tftp err_msg = "File is too large to send"; send_error(sockfd, client_addr, err_msg, ERROR_DF); cout << current_time() + client_ip + " File is too large to send. Operation aborted\n"; clean_sources(f, sockfd); return EXIT_FAILURE; } } else {// mezi parametry nebyla velikost bloku, uvazuje se standardni file_buf = new char[STANDARD_BLOCK + 1]; file_buf_size = STANDARD_BLOCK + 1; } } else {// bez parametru, posilani prvnich dat if(f->get_file_size() > MAX_BLOCK_COUNT * STANDARD_BLOCK) {// Soubor je prilis velky na prenos pres tftp err_msg = "File is too large to send"; send_error(sockfd, client_addr, err_msg, ERROR_IL); cout << current_time() + client_ip + " File is too large to send. Operation aborted\n"; clean_sources(f, sockfd); return 1; } // Alokace bufferu pro nacitani souboru - standardni velikost file_buf = new char[STANDARD_BLOCK + 1]; reading_data = true; cout << current_time() + client_ip + " Sending DATA\n"; // poslou se prvni data data_len = send_data(sockfd, client_addr, 1, STANDARD_BLOCK, f, file_buf); data_counter += data_len; if(data_len < STANDARD_BLOCK) {// Prvni blok je zaroven i posledni, tim konci cinnost reading_data = false; } } break; } case WRQ: {// Zadost o zapis get_filename(buf); // ulozim jmeno souboru // Zjistim mod prenosu mode = transfer_mode(buf); if(mode == UNKNOWN_MODE) {// neznamy mod err_msg = "Unknown transfer mode"; send_error(sockfd, client_addr, err_msg, ERROR_IL); break; } else if(mode == NETASCII) { cout << current_time() << client_ip << " Requested WRITE of " << filename << " [netascii]" << endl; } else if(mode == OCTET) { cout << current_time() << client_ip << " Requested WRITE of " << filename << " [octet]" << endl; } // Otevre se soubor pro zapis if(mode == OCTET) { file_status = f->open(filename, options->working_path(), FILE_OUT, OCTET); } else if(mode == NETASCII) { file_status = f->open(filename, options->working_path(), FILE_OUT, NETASCII); } if(file_status == FILE_EXISTS) {// Soubor j*z existuje err_msg = "File already exists"; send_error(sockfd, client_addr, err_msg, ERROR_AE); cout << current_time() << client_ip << " File " << filename << " already exists, operation aborted" << endl; clean_sources(f, sockfd); return 1; } else if(file_status == CANNOT_OPEN) {// Nelze otevrit err_msg = "Cannot open file for writing"; send_error(sockfd, client_addr, err_msg, ERROR_IL); cout << current_time() << client_ip << " Cannot opet file " << filename << " for writing, operation aborted" << endl; clean_sources(f, sockfd); return 1; } // Zjisti se rozsireni if(opt_extension(buf, numbytes, options, f) == EXTENSION) {// request obsahuje options send_oack(sockfd, client_addr); // Alokace bufferu pro nacitani souboru if(block_size.used == true) {// byla specifikovana velikost bloku file_buf = new char[block_size.value + 10]; file_buf_size = block_size.value + 10; } else {// mezi parametry nebyla velikost bloku, uvazuje se standardni file_buf = new char[STANDARD_BLOCK + 10]; file_buf_size = STANDARD_BLOCK + 10; } writing_data = true; cout << current_time() + client_ip + " Receiving DATA\n"; } else {// Bez rozsireni, zasle se ack 0 send_ack(sockfd, client_addr, 0); // Alokace bufferu pro zapis souboru - standardni velikost file_buf = new char[STANDARD_BLOCK + 10]; file_buf_size = STANDARD_BLOCK + 10; writing_data = true; cout << current_time() + client_ip + " Receiving DATA\n"; } break; } case DATA: {// Datovy paket od klienta if(writing_data == true) {// Probiha prenos block_num = ack_number(file_buf); // zjisti se cislo bloku if((block_counter + 1) != block_num) {// Prisel blok, ktery nenavazuje na predchozi err_msg = "Error while file transfer"; send_error(sockfd, client_addr, err_msg, ERROR_IL); break; } int actual_block; if(block_size.used == true) {// Pouziva se nestandardni velikost bloku actual_block = block_size.value; bytes_write = recv_data(file_buf, numbytes, actual_block, f); // zapisou se data } else {// Standardni velikost bloku actual_block = STANDARD_BLOCK; bytes_write = recv_data(file_buf, numbytes, actual_block, f); // zapisou se data } if(bytes_write >= 0) {// Zapis byl uspesny, potvrdi se klientovi send_ack(sockfd, client_addr, block_num); block_counter++; // zvetsi se pocitadlo ulozenych bloku data_counter += bytes_write; // pricte se pocet ulozenych dat k pocitadlu if((numbytes - 4) < actual_block) {// dat bylo min nez je velikost bloku writing_data = false; f->close_file(FILE_OUT); // uzavre se soubor pro zapis cout << current_time() << client_ip << " File " << filename << " has been stored [" << data_counter << " B, " << block_counter << " blocks]" << endl; clean_sources(f, sockfd); return EXIT_FAILURE; } } else if(bytes_write == READ_ERR) {// Zapis nebyl uspesny err_msg = "Error while writing to file"; send_error(sockfd, client_addr, err_msg, ERROR_IL); clean_sources(f, sockfd); return EXIT_FAILURE; } } break; } case ACK: {// Potvrzeni od klienta, ze obdrzel konkretni datovy paket ack = ack_number(buf); if(ack == 0) cout << current_time() + client_ip + " Sending DATA\n"; if(reading_data == true) {// prenos jeste nebyl dokoncen if(block_size.used == true) {// Pouziva se nestandardni velikost bloku data_len = send_data(sockfd, client_addr, ack + 1, block_size.value, f, file_buf); if(data_len == SEND_FAIL) {// Chyba pri odesilani cerr << current_time() << client_ip << " Error in sendto, operation aborted" << endl; clean_sources(f, sockfd); return EXIT_FAILURE; } else if(data_len < block_size.value) reading_data = false; } else {// Standardni velikost bloku data_len = send_data(sockfd, client_addr, ack + 1, STANDARD_BLOCK, f, file_buf); if(data_len == SEND_FAIL) {// Chyba pri odesilani cerr << current_time() << client_ip << " Error in sendto, operation aborted" << endl; clean_sources(f, sockfd); return EXIT_FAILURE; } else if(data_len < STANDARD_BLOCK) reading_data = false; } data_counter += data_len; } else {// Prenos byl dokoncen cout << current_time() << client_ip << " File " << filename << " has been sent [" << data_counter << " B, " << ack << " blocks]\n"; clean_sources(f, sockfd); return EXIT_SUCCESS; } break; } case ERR: {// Error paket int err; if(reading_data == true) {// Klient poslal error pri cteni dat err = recv_error(buf); switch(err) { case ERROR_UN: {// Unknown transfer ID f->close_file(FILE_IN); cout << current_time() << client_ip << " Client aborted file read (transport error)" << endl; return EXIT_FAILURE; } case ERROR_DF: {// Disk full f->close_file(FILE_IN); cout << current_time() << client_ip << " Client aborted file read (too large)" << endl; return EXIT_FAILURE; } case ERROR_ND: {// Nedefinovana chyba f->close_file(FILE_IN); cout << current_time() << client_ip << " Client aborted file read (undefined error)" << endl; return EXIT_FAILURE; } } } else if(writing_data == true) {// Klient poslal error pri zapisu dat err = recv_error(buf); switch(err) { case ERROR_UN: {// Unknown transfer ID f->close_file(FILE_OUT); cout << current_time() << client_ip << " Client aborted file write" << endl; return EXIT_FAILURE; } case ERROR_ND: {// Nedefinovana chyba f->close_file(FILE_OUT); cout << current_time() << client_ip << " Client aborted file read (undefined error)" << endl; return EXIT_FAILURE; } } } break; } case UNKNOWN_OPCODE: {// Neznamy opcode err_msg = "Unknown request"; send_error(sockfd, client_addr, err_msg, ERROR_IL); clean_sources(f, sockfd); return EXIT_FAILURE; } }// konec switch(type) if(writing_data == true) {// Pokud probiha zapis, pouziva se vetsi buffer alokovany na halde recv_status = recv_packet(sockfd, client_addr, file_buf, file_buf_size); } else {// Pri cteni staci mensi velikost na zasobniku recv_status = recv_packet(sockfd, client_addr, buf, BUFSIZE); } if(recv_status == TIMEOUT_ELAPSED) {// Vyprsel cas cekani, probehne 3x pokus o znovu navazani komunikace tryouts++; if(tryouts == 3) {// Probehly tri pokusy o znovu navazani komunikace cout << current_time() << client_ip << " Timeout elapsed, operation aborted" << endl; clean_sources(f, sockfd); return 1; } else {// Probehne pokus cout << current_time() << client_ip << " Timeout elapsed, retrying..." << endl; } } else {// Byl normalne prijat paket numbytes = recv_status; tryouts = 0; // Vynuluje se pocitadlo neuspesnych pokusu } } }
/** * Handle incoming PSYC message. * * @param recv Receive handle. * @param msg The message. * * @return #GNUNET_OK on success, * #GNUNET_SYSERR on receive error. */ int GNUNET_PSYC_receive_message (struct GNUNET_PSYC_ReceiveHandle *recv, const struct GNUNET_PSYC_MessageHeader *msg) { uint16_t size = ntohs (msg->header.size); uint32_t flags = ntohl (msg->flags); uint64_t message_id; GNUNET_PSYC_log_message (GNUNET_ERROR_TYPE_DEBUG, (struct GNUNET_MessageHeader *) msg); if (GNUNET_PSYC_MESSAGE_STATE_START == recv->state) { recv->message_id = GNUNET_ntohll (msg->message_id); recv->flags = flags; recv->slave_key = msg->slave_key; recv->mod_value_size = 0; recv->mod_value_size_expected = 0; } else if (GNUNET_ntohll (msg->message_id) != recv->message_id) { // FIXME LOG (GNUNET_ERROR_TYPE_WARNING, "Unexpected message ID. Got: %" PRIu64 ", expected: %" PRIu64 "\n", GNUNET_ntohll (msg->message_id), recv->message_id); GNUNET_break_op (0); recv_error (recv); return GNUNET_SYSERR; } else if (flags != recv->flags) { LOG (GNUNET_ERROR_TYPE_WARNING, "Unexpected message flags. Got: %lu, expected: %lu\n", flags, recv->flags); GNUNET_break_op (0); recv_error (recv); return GNUNET_SYSERR; } message_id = recv->message_id; uint16_t pos = 0, psize = 0, ptype, size_eq, size_min; for (pos = 0; sizeof (*msg) + pos < size; pos += psize) { const struct GNUNET_MessageHeader *pmsg = (const struct GNUNET_MessageHeader *) ((char *) &msg[1] + pos); psize = ntohs (pmsg->size); ptype = ntohs (pmsg->type); size_eq = size_min = 0; if (psize < sizeof (*pmsg) || sizeof (*msg) + pos + psize > size) { GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Dropping message of type %u with invalid size %u.\n", ptype, psize); recv_error (recv); return GNUNET_SYSERR; } GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received message part of type %u and size %u from PSYC.\n", ptype, psize); GNUNET_PSYC_log_message (GNUNET_ERROR_TYPE_DEBUG, pmsg); switch (ptype) { case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_METHOD: size_min = sizeof (struct GNUNET_PSYC_MessageMethod); break; case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_MODIFIER: size_min = sizeof (struct GNUNET_PSYC_MessageModifier); break; case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_MOD_CONT: case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_DATA: size_min = sizeof (struct GNUNET_MessageHeader); break; case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_END: case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_CANCEL: size_eq = sizeof (struct GNUNET_MessageHeader); break; default: GNUNET_break_op (0); recv_error (recv); return GNUNET_SYSERR; } if (! ((0 < size_eq && psize == size_eq) || (0 < size_min && size_min <= psize))) { GNUNET_break_op (0); recv_error (recv); return GNUNET_SYSERR; } switch (ptype) { case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_METHOD: { struct GNUNET_PSYC_MessageMethod *meth = (struct GNUNET_PSYC_MessageMethod *) pmsg; if (GNUNET_PSYC_MESSAGE_STATE_START != recv->state) { LOG (GNUNET_ERROR_TYPE_WARNING, "Dropping out of order message method (%u).\n", recv->state); /* It is normal to receive an incomplete message right after connecting, * but should not happen later. * FIXME: add a check for this condition. */ GNUNET_break_op (0); recv_error (recv); return GNUNET_SYSERR; } if ('\0' != *((char *) meth + psize - 1)) { LOG (GNUNET_ERROR_TYPE_WARNING, "Dropping message with malformed method. " "Message ID: %" PRIu64 "\n", recv->message_id); GNUNET_break_op (0); recv_error (recv); return GNUNET_SYSERR; } recv->state = GNUNET_PSYC_MESSAGE_STATE_METHOD; break; } case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_MODIFIER: { if (!(GNUNET_PSYC_MESSAGE_STATE_METHOD == recv->state || GNUNET_PSYC_MESSAGE_STATE_MODIFIER == recv->state || GNUNET_PSYC_MESSAGE_STATE_MOD_CONT == recv->state)) { LOG (GNUNET_ERROR_TYPE_WARNING, "Dropping out of order message modifier (%u).\n", recv->state); GNUNET_break_op (0); recv_error (recv); return GNUNET_SYSERR; } struct GNUNET_PSYC_MessageModifier *mod = (struct GNUNET_PSYC_MessageModifier *) pmsg; uint16_t name_size = ntohs (mod->name_size); recv->mod_value_size_expected = ntohl (mod->value_size); recv->mod_value_size = psize - sizeof (*mod) - name_size; if (psize < sizeof (*mod) + name_size || '\0' != *((char *) &mod[1] + name_size - 1) || recv->mod_value_size_expected < recv->mod_value_size) { LOG (GNUNET_ERROR_TYPE_WARNING, "Dropping malformed modifier.\n"); GNUNET_break_op (0); recv_error (recv); return GNUNET_SYSERR; } recv->state = GNUNET_PSYC_MESSAGE_STATE_MODIFIER; break; } case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_MOD_CONT: { recv->mod_value_size += psize - sizeof (*pmsg); if (!(GNUNET_PSYC_MESSAGE_STATE_MODIFIER == recv->state || GNUNET_PSYC_MESSAGE_STATE_MOD_CONT == recv->state) || recv->mod_value_size_expected < recv->mod_value_size) { LOG (GNUNET_ERROR_TYPE_WARNING, "Dropping out of order message modifier continuation " "!(%u == %u || %u == %u) || %lu < %lu.\n", GNUNET_PSYC_MESSAGE_STATE_MODIFIER, recv->state, GNUNET_PSYC_MESSAGE_STATE_MOD_CONT, recv->state, recv->mod_value_size_expected, recv->mod_value_size); GNUNET_break_op (0); recv_error (recv); return GNUNET_SYSERR; } break; } case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_DATA: { if (recv->state < GNUNET_PSYC_MESSAGE_STATE_METHOD || recv->mod_value_size_expected != recv->mod_value_size) { LOG (GNUNET_ERROR_TYPE_WARNING, "Dropping out of order message data fragment " "(%u < %u || %lu != %lu).\n", recv->state, GNUNET_PSYC_MESSAGE_STATE_METHOD, recv->mod_value_size_expected, recv->mod_value_size); GNUNET_break_op (0); recv_error (recv); return GNUNET_SYSERR; } recv->state = GNUNET_PSYC_MESSAGE_STATE_DATA; break; } } if (NULL != recv->message_part_cb) recv->message_part_cb (recv->cb_cls, &recv->slave_key, recv->message_id, recv->flags, 0, // FIXME: data_offset pmsg); switch (ptype) { case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_END: case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_CANCEL: GNUNET_PSYC_receive_reset (recv); break; } } if (NULL != recv->message_cb) recv->message_cb (recv->cb_cls, message_id, flags, msg); return GNUNET_OK; }