struct rtp_recorder_t* rtp_recorder_create(crypt_aes_p crypt, bool disable_audio, audio_queue_p audio_queue, struct sockaddr* local_end_point, struct sockaddr* remote_end_point, uint16_t remote_control_port, uint16_t remote_timing_port) { struct rtp_recorder_t* rr = (struct rtp_recorder_t*)malloc(sizeof(struct rtp_recorder_t)); bzero(rr, sizeof(struct rtp_recorder_t)); rr->crypt = crypt; rr->disable_audio = disable_audio; rr->audio_queue = audio_queue; rr->timer_mutex = mutex_create(); rr->timer_cond = condition_create(); rr->remote_control_end_point = sockaddr_copy(remote_end_point); rr->remote_timing_end_point = sockaddr_copy(remote_end_point); sockaddr_set_port(rr->remote_control_end_point, remote_control_port); sockaddr_set_port(rr->remote_timing_end_point, remote_timing_port); rr->streaming_socket = _rtp_recorder_create_socket(rr, "Straming socket", local_end_point, remote_end_point); rr->control_socket = _rtp_recorder_create_socket(rr, "Control socket", local_end_point, remote_end_point); rr->timing_socket = _rtp_recorder_create_socket(rr, "Timing socket", local_end_point, remote_end_point); return rr; }
static int init_udp(struct qtsession* session) { char* envval; fprintf(stderr, "Initializing UDP socket...\n"); struct addrinfo *ai_local = NULL, *ai_remote = NULL; unsigned short af = 0; int ret; if (envval = getconf("LOCAL_ADDRESS")) { if (ret = getaddrinfo(envval, NULL, NULL, &ai_local)) return errorexit2("getaddrinfo(LOCAL_ADDRESS)", gai_strerror(ret)); if (!ai_local) return errorexit("LOCAL_ADDRESS lookup failed"); if (ai_local->ai_addrlen > sizeof(sockaddr_any)) return errorexit("Resolved LOCAL_ADDRESS is too big"); af = ai_local->ai_family; } if (envval = getconf("REMOTE_ADDRESS")) { if (ret = getaddrinfo(envval, NULL, NULL, &ai_remote)) return errorexit2("getaddrinfo(REMOTE_ADDRESS)", gai_strerror(ret)); if (!ai_remote) return errorexit("REMOTE_ADDRESS lookup failed"); if (ai_remote->ai_addrlen > sizeof(sockaddr_any)) return errorexit("Resolved REMOTE_ADDRESS is too big"); if (af && af != ai_remote->ai_family) return errorexit("Address families do not match"); af = ai_remote->ai_family; } if (!af) af = AF_INET; int sfd = socket(af, SOCK_DGRAM, IPPROTO_UDP); if (sfd < 0) return errorexitp("Could not create UDP socket"); sockaddr_any udpaddr; memset(&udpaddr, 0, sizeof(udpaddr)); udpaddr.any.sa_family = af; if (ai_local) memcpy(&udpaddr, ai_local->ai_addr, ai_local->ai_addrlen); int port = 2998; if (envval = getconf("LOCAL_PORT")) port = atoi(envval); if (sockaddr_set_port(&udpaddr, port)) return -1; if (bind(sfd, (struct sockaddr*)&udpaddr, sizeof(udpaddr))) return errorexitp("Could not bind socket"); memset(&udpaddr, 0, sizeof(udpaddr)); udpaddr.any.sa_family = af; if (ai_remote) memcpy(&udpaddr, ai_remote->ai_addr, ai_remote->ai_addrlen); if (!ai_remote || sockaddr_is_zero_address(&udpaddr)) { session->remote_float = 1; } else { session->remote_float = getconf("REMOTE_FLOAT") ? 1 : 0; port = 2998; if (envval = getconf("REMOTE_PORT")) port = atoi(envval); if (sockaddr_set_port(&udpaddr, port)) return -1; session->remote_addr = udpaddr; if (session->remote_float) { session->remote_float = 2; } else { if (connect(sfd, (struct sockaddr*)&udpaddr, sizeof(udpaddr))) return errorexitp("Could not connect socket"); } } if (ai_local) freeaddrinfo(ai_local); if (ai_remote) freeaddrinfo(ai_remote); session->fd_socket = sfd; return sfd; }
static int add_addresses_to_addrset_1 (struct addrset *as, const char *ip, int port_mode, const char *msgtag) { char buf[INET6_ADDRSTRLEN_EXTENDED]; os_sockaddr_storage addr; if (!os_sockaddrStringToAddress (ip, (os_sockaddr *) &addr, !config.useIpv6)) { NN_WARNING2 ("%s: %s: not a valid address\n", msgtag, ip); return -1; } if (port_mode >= 0) { sockaddr_set_port (&addr, port_mode); nn_log (LC_CONFIG, "%s: add %s", msgtag, sockaddr_to_string_with_port (buf, &addr)); add_to_addrset (as, &addr); } else { sockaddr_set_port (&addr, 0); nn_log (LC_CONFIG, "%s: add ", msgtag); if (!is_mcaddr (&addr)) { int i; for (i = 0; i < 10; i++) { int port = config.port_base + config.port_dg * config.domainId + i * config.port_pg + config.port_d1; sockaddr_set_port (&addr, port); if (i == 0) nn_log (LC_CONFIG, "%s", sockaddr_to_string_with_port (buf, &addr)); else nn_log (LC_CONFIG, ", :%d", port); add_to_addrset (as, &addr); } } else { int port = port_mode; if (port == -1) port = config.port_base + config.port_dg * config.domainId + config.port_d0; sockaddr_set_port (&addr, port); nn_log (LC_CONFIG, "%s", sockaddr_to_string_with_port (buf, &addr)); add_to_addrset (as, &addr); } } nn_log (LC_CONFIG, "\n"); return 0; }
void process_dest_unreach(struct tcptable *table, char *packet, char *ifname) { struct iphdr *ip; struct ip6_hdr *ip6; struct tcphdr *tcp; struct tcptableent *tcpentry; ip = (struct iphdr *) (packet + 8); /* * Timeout checking won't be performed either, so we just pass 0 * as timeout variable. */ if (ip->version == 6) { ip6 = (struct ip6_hdr *) (packet + 8); if (ip6->ip6_nxt != IPPROTO_TCP) return; tcp = (struct tcphdr *) (packet + 48); struct sockaddr_storage saddr, daddr; sockaddr_make_ipv6(&saddr, &ip6->ip6_src); sockaddr_set_port(&saddr, ntohs(tcp->source)); sockaddr_make_ipv6(&daddr, &ip6->ip6_dst); sockaddr_set_port(&daddr, ntohs(tcp->dest)); tcpentry = in_table(table, &saddr, &daddr, ifname, 0, NULL, 0); } else { if (ip->protocol != IPPROTO_TCP) return; tcp = (struct tcphdr *) (packet + 8 + (ip->ihl * 4)); struct sockaddr_storage saddr, daddr; sockaddr_make_ipv4(&saddr, ip->saddr); sockaddr_set_port(&saddr, ntohs(tcp->source)); sockaddr_make_ipv4(&daddr, ip->daddr); sockaddr_set_port(&daddr, ntohs(tcp->dest)); tcpentry = in_table(table, &saddr, &daddr, ifname, 0, NULL, 0); } if (tcpentry != NULL) { tcpentry->stat = tcpentry->oth_connection->stat = FLAG_RST; addtoclosedlist(table, tcpentry); } }
rtp_socket_p _rtp_recorder_create_socket(struct rtp_recorder_t* rr, const char* name, struct sockaddr* local_end_point, struct sockaddr* remote_end_point) { rtp_socket_p ret = rtp_socket_create(name, remote_end_point); unsigned short p; for (p = 6000 ; p < 6100 ; p++) { struct sockaddr* ep = sockaddr_copy(local_end_point); sockaddr_set_port(ep, p); if (rtp_socket_setup(ret, ep)) { rtp_socket_set_data_received_callback(ret, _rtp_recorder_socket_data_received_callback, rr); log_message(LOG_INFO, "Setup socket on port %u", p); sockaddr_destroy(ep); return ret; } sockaddr_destroy(ep); } log_message(LOG_ERROR, "Unable to bind socket."); rtp_socket_destroy(ret); return NULL; }
/* * Receive a file. This is implemented as a state machine using a while loop * and a switch statement. This follow pxe specification pages 32-34. */ int tftp_mtftp_receive_file(struct client_data *data) { int state = S_SEND_REQ; /* current state in the state machine */ int timeout_state = state; /* what state should we go on when timeout */ int result; int block_number = 0; int last_block_number = -1;/* block number of last block for multicast */ int data_size; /* size of data received */ int sockfd = data->sockfd; /* just to simplify calls */ int sock; struct sockaddr_storage sa; /* a copy of data.sa_peer */ struct sockaddr_storage from; char from_str[SOCKADDR_PRINT_ADDR_LEN]; struct tftphdr *tftphdr = (struct tftphdr *)data->data_buffer; FILE *fp = NULL; /* the local file pointer */ int number_of_timeout = 0; int timeout = 0; struct sockaddr_storage sa_mcast_group; int mcast_sockfd = 0; struct sockaddr_storage sa_mcast; union ip_mreq_storage mreq; struct addrinfo hints, *addrinfo; int err; int mode = LISTEN; unsigned int file_bitmap[NB_BLOCK]; char string[MAXLEN]; data->file_size = 0; tftp_cancel = 0; memset(&from, 0, sizeof(from)); memset(&sa_mcast, 0, sizeof(struct sockaddr_storage)); memset(&file_bitmap, 0, sizeof(file_bitmap)); /* make sure the socket is not connected */ sa.ss_family = AF_UNSPEC; connect(sockfd, (struct sockaddr *)&sa, sizeof(sa)); /* copy sa_peer structure */ memcpy(&sa, &data->sa_peer, sizeof(sa)); /* check to see if conversion is requiered */ if (strcasecmp(data->tftp_options[OPT_MODE].value, "netascii") == 0) fprintf(stderr, "netascii convertion ignored\n"); /* make sure the data buffer is SEGSIZE + 4 bytes */ if (data->data_buffer_size != (SEGSIZE + 4)) { data->data_buffer = realloc(data->data_buffer, SEGSIZE + 4); tftphdr = (struct tftphdr *)data->data_buffer; if (data->data_buffer == NULL) { fprintf(stderr, "atftp: memory allocation failure.\n"); exit(1); } data->data_buffer_size = SEGSIZE + 4; } /* open the file for writing */ if ((fp = fopen(data->local_file, "w")) == NULL) { fprintf(stderr, "atftp: can't open %s for writing.\n", data->local_file); return ERR; } /* Configure multicast stuff, look up the host */ /* if valid, update s_inn structure */ memset(&hints, 0, sizeof(hints)); hints.ai_socktype = SOCK_DGRAM; if (!getaddrinfo(data->mtftp_mcast_ip, NULL, &hints, &addrinfo) && !sockaddr_set_addrinfo(&sa_mcast_group, addrinfo)) { freeaddrinfo(addrinfo); if (!sockaddr_is_multicast(&sa_mcast_group)) { fprintf(stderr, "mtftp: bad multicast address %s\n", data->mtftp_mcast_ip); exit(1); } } else { fprintf(stderr, "atftp: bad multicast address %s", data->mtftp_mcast_ip); exit(1); } /* we need to open a new socket for multicast */ if ((mcast_sockfd = socket(AF_INET, SOCK_DGRAM, 0))<0) { perror("atftp: socket"); exit(1); } memset(&sa_mcast, 0, sizeof(sa_mcast)); sa_mcast.ss_family = sa_mcast_group.ss_family; sockaddr_set_port(&sa, data->mtftp_client_port); if (bind(mcast_sockfd, (struct sockaddr *)&sa_mcast, sizeof(sa_mcast)) < 0) { perror("atftp: bind"); exit(1); } sockaddr_get_mreq(&sa_mcast_group, &mreq); if (sa_mcast_group.ss_family == AF_INET) err = setsockopt(mcast_sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq.v4, sizeof(mreq.v4)); else err = setsockopt(mcast_sockfd, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mreq.v6, sizeof(mreq.v6)); if (err < 0) { perror("atftp: setsockopt"); exit(1); } state = S_LISTEN; while (1) { #ifdef DEBUG if (data->delay) usleep(data->delay*1000); #endif if (tftp_cancel) { if (from.ss_family == 0) state = S_ABORT; else { if (mode == RECEIVE) { tftp_send_error(sockfd, &sa, EUNDEF, data->data_buffer, data->data_buffer_size); if (data->trace) fprintf(stderr, "sent ERROR <code: %d, msg: %s>\n", EUNDEF, tftp_errmsg[EUNDEF]); } state = S_ABORT; } tftp_cancel = 0; } switch (state) { case S_LISTEN: if (data->trace) fprintf(stderr, "mtftp: listening for ongoing transfer on %s port %d\n", data->mtftp_mcast_ip, data->mtftp_client_port); number_of_timeout = 0; mode = LISTEN; if (last_block_number > 0) { timeout = data->mtftp_listen_delay - tftp_mtftp_missed_packet(file_bitmap, last_block_number, 1); if (timeout < 0) timeout = 0; } else timeout = data->mtftp_listen_delay; state = S_WAIT_PACKET; timeout_state = S_OPEN; break; case S_OPEN: if (data->trace) fprintf(stderr, "mtftp: opening new connection\n"); mode = OPEN; block_number = 0; timeout = data->mtftp_timeout_delay; state = S_SEND_REQ; break; case S_RECEIVE: if (data->trace) fprintf(stderr, "mtftp: connected, receiving\n"); mode = RECEIVE; timeout = data->mtftp_timeout_delay; state = S_SEND_ACK; break; case S_SEND_REQ: timeout_state = S_SEND_REQ; if (data->trace) { opt_options_to_string(data->tftp_options, string, MAXLEN); fprintf(stderr, "sent RRQ <file: %s, mode: %s <%s>>\n", data->tftp_options[OPT_FILENAME].value, data->tftp_options[OPT_MODE].value, string); } /* send request packet */ if (tftp_send_request(sockfd, &sa, RRQ, data->data_buffer, data->data_buffer_size, data->tftp_options) == ERR) state = S_ABORT; else state = S_WAIT_PACKET; sockaddr_set_port(&sa, 0); /* must be set to 0 before the fist call to tftp_get_packet, but it was set before the call to tftp_send_request with the server port */ break; case S_SEND_ACK: timeout_state = S_SEND_ACK; /* walk the bitmap to find the next missing block */ //prev_bitmap_hole = //tftp_find_bitmap_hole(prev_bitmap_hole, file_bitmap); //block_number = prev_bitmap_hole; if (data->trace) fprintf(stderr, "sent ACK <block: %d>\n", block_number); tftp_send_ack(sockfd, &sa, block_number); /* if we just ACK the last block we are done */ if (block_number == last_block_number) state = S_END; else state = S_WAIT_PACKET; break; case S_WAIT_PACKET: data_size = data->data_buffer_size; /* receive the data */ result = tftp_get_packet(sockfd, mcast_sockfd, &sock, &sa, &from, NULL, timeout, &data_size, data->data_buffer); switch (result) { case GET_TIMEOUT: number_of_timeout++; if (mode == LISTEN) { fprintf(stderr, "mtftp: timeout while listening\n"); state = S_OPEN; } else { fprintf(stderr, "mtftp: timeout: retrying...\n"); if (number_of_timeout > NB_OF_RETRY) state = S_ABORT; else state = timeout_state; } break; case GET_ERROR: if (mode == LISTEN) { fprintf(stderr, "mtftp: unexpected error received from server, ignoring\n"); break; } /* Can only receive this error from unicast */ if (!sockaddr_equal_addr(&sa, &from)) { fprintf(stderr, "mtftp: error packet discarded from <%s>.\n", sockaddr_print_addr(&from, from_str, sizeof(from_str))); break; } /* packet is for us */ fprintf(stderr, "mtftp: error received from server"); fwrite(tftphdr->th_msg, 1, data_size - 4 - 1, stderr); fprintf(stderr, ">\n"); state = S_ABORT; break; case GET_DATA: /* Specification state that server source IP must matches, but port is not a requierement (anyway we may not know the source port yet) */ if (sockaddr_equal_addr(&sa, &from)) { if (mode != LISTEN) { if (sock == sockfd) { /* This is a unicast packet from the server. This should happend for the first data block only, when current block number is 0 and when in OPEN mode */ if ((block_number > 0) || (mode != OPEN)) fprintf(stderr, "mtftp: unexpected unicast packet from <%s>," " continuing\n", sockaddr_print_addr(&from, from_str, sizeof(from_str))); else mode = RECEIVE; } else { /* We receive data on the multicast socket, it should happend for packets 1 and above */ if (block_number == 0) { mode = LISTEN; fprintf(stderr, "mtftp: got multicast data packet," " falling back to listen mode\n"); } } } else { /* We are in listenning mode, we expect data on multicast socket only */ if (sock == sockfd) { fprintf(stderr, "mtftp: unexpected unicast packet from <%s>.\n", sockaddr_print_addr(&from, from_str, sizeof(from_str))); break; } } } else { fprintf(stderr, "mtftp: unexpected packet from <%s>\n", sockaddr_print_addr(&from, from_str, sizeof(from_str))); break; } number_of_timeout = 0; state = S_DATA_RECEIVED; break; case GET_DISCARD: /* consider discarded packet as timeout to make sure when don't lock up when doing multicast transfer and routing is broken or when using wrong mcast IP or port */ number_of_timeout++; fprintf(stderr, "mtftp: packet discard <%s>.\n", sockaddr_print_addr(&from, from_str, sizeof(from_str))); if (number_of_timeout > NB_OF_RETRY) state = S_ABORT; break; case ERR: fprintf(stderr, "mtftp: unknown error.\n"); state = S_ABORT; break; default: fprintf(stderr, "mtftp: abnormal return value %d.\n", result); } break; case S_DATA_RECEIVED: block_number = ntohs(tftphdr->th_block); if (data->trace) fprintf(stderr, "received DATA <block: %d, size: %d>\n", ntohs(tftphdr->th_block), data_size - 4); fseek(fp, (block_number - 1) * (data->data_buffer_size - 4), SEEK_SET); if (fwrite(tftphdr->th_data, 1, data_size - 4, fp) != (data_size - 4)) { fprintf(stderr, "mtftp: error writing to file %s\n", data->local_file); if (mode == RECEIVE) tftp_send_error(sockfd, &sa, ENOSPACE, data->data_buffer, data->data_buffer_size); state = S_END; break; } data->file_size += data_size; /* FIXME: not usefull */ /* Record the block number of the last block. The last block is the one with less data than the transfer block size */ if (data_size < data->data_buffer_size) last_block_number = block_number; /* Mark the received block in the bitmap */ file_bitmap[(block_number - 1)/32] |= (1 << ((block_number - 1) % 32)); /* if we are the master client we ack, else we just wait for data */ if (mode == LISTEN) { /* If we've not received all packets, continue listen. In the case we've not seen the last packet yet, no choice but continuing listen phase and eventually fall back to the open mode and download the whole file again. If we've seen the last packet, we also continue listen, but if we've got all the file we are done */ if (last_block_number < 0) state = S_WAIT_PACKET; else { if (tftp_mtftp_missed_packet(file_bitmap, last_block_number, 0)) state = S_WAIT_PACKET; else { fprintf(stderr, "mtftp: got all packets\n"); state = S_END; } } } else state = S_SEND_ACK; break; case S_END: case S_ABORT: /* close file */ if (fp) fclose(fp); /* drop multicast membership */ if (sa_mcast_group.ss_family == AF_INET) err = setsockopt(mcast_sockfd, IPPROTO_IP, IP_DROP_MEMBERSHIP, &mreq.v4, sizeof(mreq.v4)); else err = setsockopt(mcast_sockfd, IPPROTO_IPV6, IPV6_DROP_MEMBERSHIP, &mreq.v6, sizeof(mreq.v6)); if (err < 0) { perror("setsockopt"); exit(1); } /* close socket */ if (mcast_sockfd) close(mcast_sockfd); /* return proper error code */ if (state == S_END) return OK; else fprintf(stderr, "mtftp: aborting\n"); default: return ERR; } } }
/* * Send a file. It is implemented as a state machine using a while loop * and a switch statement. Function flow is as follow: * - sanity check * - check client's request * - enter state machine * * 1) send a DATA or OACK * 2) wait replay * - if ACK, goto 3 * - if ERROR abort * - if TIMEOUT goto previous state * 3) send data, goto 2 */ int tftpd_send_file(struct thread_data *data) { int state = S_BEGIN; int timeout_state = state; int result; int block_number = 0; int last_block = -1; int data_size; struct sockaddr_storage *sa = &data->client_info->client; struct sockaddr_storage from; char addr_str[SOCKADDR_PRINT_ADDR_LEN]; int sockfd = data->sockfd; struct tftphdr *tftphdr = (struct tftphdr *)data->data_buffer; FILE *fp; char filename[MAXLEN]; char string[MAXLEN]; int timeout = data->timeout; int number_of_timeout = 0; int mcast_switch = data->mcast_switch_client; struct stat file_stat; int convert = 0; /* if true, do netascii conversion */ struct thread_data *thread = NULL; /* used when looking for a multicast thread */ int multicast = 0; /* set to 1 if multicast */ struct client_info *client_info = data->client_info; struct client_info *client_old = NULL; struct tftp_opt options[OPT_NUMBER]; int prev_block_number = 0; /* needed to support netascii convertion */ int prev_file_pos = 0; int temp = 0; /* look for mode option */ if (strcasecmp(data->tftp_options[OPT_MODE].value, "netascii") == 0) { convert = 1; logger(LOG_DEBUG, "will do netascii convertion"); } /* file name verification */ Strncpy(filename, data->tftp_options[OPT_FILENAME].value, MAXLEN); if (tftpd_rules_check(filename) != OK) { tftp_send_error(sockfd, sa, EACCESS, data->data_buffer, data->data_buffer_size); if (data->trace) logger(LOG_DEBUG, "sent ERROR <code: %d, msg: %s>", EACCESS, tftp_errmsg[EACCESS]); return ERR; } /* verify that the requested file exist */ fp = fopen(filename, "r"); #ifdef HAVE_PCRE if (fp == NULL) { /* Verify if this file have a working subsitution */ if (pcre_top != NULL) { if (tftpd_pcre_sub(pcre_top, string, MAXLEN, data->tftp_options[OPT_FILENAME].value) < 0) { logger(LOG_DEBUG, "PCRE failed to match"); } else { logger(LOG_INFO, "PCRE mapped %s -> %s", data->tftp_options[OPT_FILENAME].value, string); Strncpy(filename, string, MAXLEN); /* recheck those rules */ if (tftpd_rules_check(filename) != OK) { tftp_send_error(sockfd, sa, EACCESS, data->data_buffer, data->data_buffer_size); if (data->trace) logger(LOG_DEBUG, "sent ERROR <code: %d, msg: %s>", EACCESS, tftp_errmsg[EACCESS]); return ERR; } /* write back the new file name to the option structure */ opt_set_options(data->tftp_options, "filename", filename); /* try to open this new file */ fp = fopen(filename, "r"); } } } #endif if (fp == NULL) { tftp_send_error(sockfd, sa, ENOTFOUND, data->data_buffer, data->data_buffer_size); logger(LOG_INFO, "File %s not found", filename); if (data->trace) logger(LOG_DEBUG, "sent ERROR <code: %d, msg: %s>", ENOTFOUND, tftp_errmsg[ENOTFOUND]); return ERR; } /* To return the size of the file with tsize argument */ fstat(fileno(fp), &file_stat); /* tsize option */ if ((opt_get_tsize(data->tftp_options) > -1) && !convert) { opt_set_tsize(file_stat.st_size, data->tftp_options); logger(LOG_INFO, "tsize option -> %d", file_stat.st_size); } /* timeout option */ if ((result = opt_get_timeout(data->tftp_options)) > -1) { if ((result < 1) || (result > 255)) { tftp_send_error(sockfd, sa, EOPTNEG, data->data_buffer, data->data_buffer_size); if (data->trace) logger(LOG_DEBUG, "sent ERROR <code: %d, msg: %s>", EOPTNEG, tftp_errmsg[EOPTNEG]); fclose(fp); return ERR; } timeout = result; opt_set_timeout(timeout, data->tftp_options); logger(LOG_INFO, "timeout option -> %d", timeout); } /* blksize options */ if ((result = opt_get_blksize(data->tftp_options)) > -1) { if ((result < 8) || (result > 65464)) { tftp_send_error(sockfd, sa, EOPTNEG, data->data_buffer, data->data_buffer_size); if (data->trace) logger(LOG_DEBUG, "sent ERROR <code: %d, msg: %s>", EOPTNEG, tftp_errmsg[EOPTNEG]); fclose(fp); return ERR; } data->data_buffer_size = result + 4; data->data_buffer = realloc(data->data_buffer, data->data_buffer_size); if (data->data_buffer == NULL) { logger(LOG_ERR, "memory allocation failure"); fclose(fp); return ERR; } tftphdr = (struct tftphdr *)data->data_buffer; if (data->data_buffer == NULL) { tftp_send_error(sockfd, sa, ENOSPACE, data->data_buffer, data->data_buffer_size); if (data->trace) logger(LOG_DEBUG, "sent ERROR <code: %d, msg: %s>", ENOSPACE, tftp_errmsg[ENOSPACE]); fclose(fp); return ERR; } opt_set_blksize(result, data->tftp_options); logger(LOG_INFO, "blksize option -> %d", result); } /* Verify that the file can be sent in 2^16 block of BLKSIZE octets */ if ((file_stat.st_size / (data->data_buffer_size - 4)) > 65535) { tftp_send_error(sockfd, sa, EUNDEF, data->data_buffer, data->data_buffer_size); logger(LOG_NOTICE, "Requested file to big, increase BLKSIZE"); if (data->trace) logger(LOG_DEBUG, "sent ERROR <code: %d, msg: %s>", EUNDEF, tftp_errmsg[EUNDEF]); fclose(fp); return ERR; } /* multicast option */ if (data->tftp_options[OPT_MULTICAST].specified && data->tftp_options[OPT_MULTICAST].enabled && !convert) { /* * Find a server with the same options to give up the client. */ logger(LOG_DEBUG, "Searching a server thread to give up this client"); result = tftpd_list_find_multicast_server_and_add(&thread, data, data->client_info); if ( result > 0) { /* add this client to its list of client */ if (result == 1) logger(LOG_DEBUG, "Added client %p to thread %p", data->client_info, thread); else logger(LOG_DEBUG, "Client (%p) is already in list of thread %p", data->client_info, thread); /* NULL our own pointer so we don't free memory */ if (result == 1) data->client_info = NULL; /* Look at needed information to oack that client */ opt_set_multicast(data->tftp_options, thread->mc_addr, thread->mc_port, 0); logger(LOG_INFO, "multicast option -> %s,%d,%d", thread->mc_addr, thread->mc_port, 0); /* Send an OACK to that client. There is a possible race condition here where the new server thread OACK this client before us. This should not be a problem: the client thread will receive a second OACK and fall back to non master mode. Then the server will timeout and either resend OACK or continu with the next client */ opt_options_to_string(data->tftp_options, string, MAXLEN); if (data->trace) logger(LOG_DEBUG, "sent OACK <%s>", string); tftp_send_oack(thread->sockfd, sa, data->tftp_options, data->data_buffer, data->data_buffer_size); /* We are done */ logger(LOG_INFO, "Client transfered to %p", thread); fclose(fp); return OK; } else { struct addrinfo hints, *result; /* configure socket, get an IP address */ if (tftpd_mcast_get_tid(&data->mc_addr, &data->mc_port) != OK) { logger(LOG_ERR, "No multicast address/port available"); fclose(fp); return ERR; } logger(LOG_DEBUG, "mcast_addr: %s, mcast_port: %d", data->mc_addr, data->mc_port); /* convert address */ memset(&hints, 0, sizeof(hints)); hints.ai_socktype = SOCK_DGRAM; hints.ai_flags = AI_NUMERICHOST; if (getaddrinfo(data->mc_addr, NULL, &hints, &result) || sockaddr_set_addrinfo(&data->sa_mcast, result)) { logger(LOG_ERR, "bad address %s\n",data->mc_addr); fclose(fp); return ERR; } freeaddrinfo(result); sockaddr_set_port(&data->sa_mcast, data->mc_port); /* verify address is multicast */ if (!sockaddr_is_multicast(&data->sa_mcast)) { logger(LOG_ERR, "bad multicast address %s\n", sockaddr_print_addr(&data->sa_mcast, addr_str, sizeof(addr_str))); fclose(fp); return ERR; } /* initialise multicast address structure */ sockaddr_get_mreq(&data->sa_mcast, &data->mcastaddr); if (data->sa_mcast.ss_family == AF_INET) setsockopt(data->sockfd, IPPROTO_IP, IP_MULTICAST_TTL, &data->mcast_ttl, sizeof(data->mcast_ttl)); else setsockopt(data->sockfd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &data->mcast_ttl, sizeof(data->mcast_ttl)); /* set options data for OACK */ opt_set_multicast(data->tftp_options, data->mc_addr, data->mc_port, 1); logger(LOG_INFO, "multicast option -> %s,%d,%d", data->mc_addr, data->mc_port, 1); /* the socket must be unconnected for multicast */ sa->ss_family = AF_UNSPEC; connect(sockfd, (struct sockaddr *)sa, sizeof(*sa)); /* set multicast flag */ multicast = 1; /* Now ready to receive new clients */ tftpd_clientlist_ready(data); } } /* copy options to local structure, used when falling back a client to slave */ memcpy(options, data->tftp_options, sizeof(options)); opt_set_multicast(options, data->mc_addr, data->mc_port, 0); /* That's it, ready to send the file */ while (1) { if (tftpd_cancel) { /* Send error to all client */ logger(LOG_DEBUG, "thread cancelled"); do { tftpd_clientlist_done(data, client_info, NULL); tftp_send_error(sockfd, &client_info->client, EUNDEF, data->data_buffer, data->data_buffer_size); if (data->trace) { logger(LOG_DEBUG, "sent ERROR <code: %d, msg: %s> to %s", EUNDEF, tftp_errmsg[EUNDEF], sockaddr_print_addr(&client_info->client, addr_str, sizeof(addr_str))); } } while (tftpd_clientlist_next(data, &client_info) == 1); state = S_ABORT; } switch (state) { case S_BEGIN: if (opt_support_options(data->tftp_options)) state = S_SEND_OACK; else state = S_SEND_DATA; break; case S_SEND_OACK: timeout_state = state; opt_options_to_string(data->tftp_options, string, MAXLEN); if (data->trace) logger(LOG_DEBUG, "sent OACK <%s>", string); tftp_send_oack(sockfd, sa, data->tftp_options, data->data_buffer, data->data_buffer_size); state = S_WAIT_PACKET; break; case S_SEND_DATA: timeout_state = state; data_size = tftp_file_read(fp, tftphdr->th_data, data->data_buffer_size - 4, block_number, convert, &prev_block_number, &prev_file_pos, &temp); data_size += 4; /* need to consider tftp header */ /* record the last block number */ if (feof(fp)) last_block = block_number; if (multicast) { tftp_send_data(sockfd, &data->sa_mcast, block_number + 1, data_size, data->data_buffer); } else { tftp_send_data(sockfd, sa, block_number + 1, data_size, data->data_buffer); } if (data->trace) logger(LOG_DEBUG, "sent DATA <block: %d, size %d>", block_number + 1, data_size - 4); state = S_WAIT_PACKET; break; case S_WAIT_PACKET: data_size = data->data_buffer_size; result = tftp_get_packet(sockfd, -1, NULL, sa, &from, NULL, timeout, &data_size, data->data_buffer); switch (result) { case GET_TIMEOUT: number_of_timeout++; if (number_of_timeout > NB_OF_RETRY) { logger(LOG_INFO, "client (%s) not responding", sockaddr_print_addr(&client_info->client, addr_str, sizeof(addr_str))); state = S_END; } else { /* The client failed to ACK our last packet. Send an OACK with mc=0 to it, fetch the next client in the list and continu with this new one */ if (multicast && mcast_switch) { client_old = client_info; tftpd_clientlist_next(data, &client_info); if (client_info && (client_info != client_old)) { /* Send an OACK to the old client remove is master client status */ opt_options_to_string(options, string, MAXLEN); if (data->trace) logger(LOG_DEBUG, "sent OACK <%s>", string); tftp_send_oack(sockfd, sa, options, data->data_buffer, data->data_buffer_size); /* Proceed normally with the next client, going to OACK state */ logger(LOG_INFO, "Serving next client: %s:%d", sockaddr_print_addr( &client_info->client, addr_str, sizeof(addr_str)), sockaddr_get_port( &client_info->client)); sa = &client_info->client; state = S_SEND_OACK; break; } else if (client_info == NULL) { /* we got a big problem if this happend */ logger(LOG_ERR, "%s: %d: abnormal condition", __FILE__, __LINE__); state = S_ABORT; break; } } logger(LOG_WARNING, "timeout: retrying..."); state = timeout_state; } break; case GET_ACK: /* handle case where packet come from un unexpected client */ if (multicast) { if (!sockaddr_equal(sa, &from)) { /* We got an ACK from a client that is not the master client. * If this is an ACK for the last block, mark this client as * done */ if ((last_block != -1) && (block_number > last_block)) { if (tftpd_clientlist_done(data, NULL, &from) == 1) logger(LOG_DEBUG, "client done <%s>", sockaddr_print_addr( &from, addr_str, sizeof(addr_str))); else logger(LOG_WARNING, "packet discarded <%s:%d>", sockaddr_print_addr( &from, addr_str, sizeof(addr_str)), sockaddr_get_port(&from)); } else /* If not, send and OACK with mc=0 to shut it up. */ { opt_options_to_string(options, string, MAXLEN); if (data->trace) logger(LOG_DEBUG, "sent OACK <%s>", string); tftp_send_oack(sockfd, &from, options, data->data_buffer, data->data_buffer_size); } break; } } else { /* check that the packet is from the current client */ if (sockaddr_get_port(sa) != sockaddr_get_port(&from)) { if (data->checkport) { logger(LOG_WARNING, "packet discarded <%s:%d>", sockaddr_print_addr(&from, addr_str, sizeof(addr_str)), sockaddr_get_port(&from)); break; } else logger(LOG_WARNING, "source port mismatch, check bypassed"); } } /* The ACK is from the current client */ number_of_timeout = 0; block_number = ntohs(tftphdr->th_block); if (data->trace) logger(LOG_DEBUG, "received ACK <block: %d>", block_number); if ((last_block != -1) && (block_number > last_block)) { state = S_END; break; } state = S_SEND_DATA; break; case GET_ERROR: /* handle case where packet come from un unexpected client */ if (multicast) { /* if packet is not from the current master client */ if (!sockaddr_equal(sa, &from)) { /* mark this client done */ if (tftpd_clientlist_done(data, NULL, &from) == 1) { if (data->trace) logger(LOG_DEBUG, "client sent ERROR, mark as done <%s>", sockaddr_print_addr( &from, addr_str, sizeof(addr_str))); } else logger(LOG_WARNING, "packet discarded <%s>", sockaddr_print_addr(&from, addr_str, sizeof(addr_str))); /* current state is unchanged */ break; } } else { /* check that the packet is from the current client */ if (sockaddr_get_port(sa) != sockaddr_get_port(&from)) { if (data->checkport) { logger(LOG_WARNING, "packet discarded <%s>", sockaddr_print_addr(&from, addr_str, sizeof(addr_str))); break; } else logger(LOG_WARNING, "source port mismatch, check bypassed"); } } /* Got an ERROR from the current master client */ Strncpy(string, tftphdr->th_msg, (((data_size - 4) > MAXLEN) ? MAXLEN : (data_size - 4))); if (data->trace) logger(LOG_DEBUG, "received ERROR <code: %d, msg: %s>", ntohs(tftphdr->th_code), string); if (multicast) { logger(LOG_DEBUG, "Marking client as done"); state = S_END; } else state = S_ABORT; break; case GET_DISCARD: /* FIXME: should we increment number_of_timeout */ logger(LOG_WARNING, "packet discarded <%s>", sockaddr_print_addr(&from, addr_str, sizeof(addr_str))); break; case ERR: logger(LOG_ERR, "%s: %d: recvfrom: %s", __FILE__, __LINE__, strerror(errno)); state = S_ABORT; break; default: logger(LOG_ERR, "%s: %d: abnormal return value %d", __FILE__, __LINE__, result); } break; case S_END: if (multicast) { logger(LOG_DEBUG, "End of multicast transfer"); /* mark the current client done */ tftpd_clientlist_done(data, client_info, NULL); /* Look if there is another client to serve. We lock list of client to make sure no other thread try to add clients in our back */ if (tftpd_clientlist_next(data, &client_info) == 1) { logger(LOG_INFO, "Serving next client: %s:%d", sockaddr_print_addr(&client_info->client, addr_str, sizeof(addr_str)), sockaddr_get_port(&client_info->client)); /* client is a new client structure */ sa = &client_info->client; /* nedd to send an oack to that client */ state = S_SEND_OACK; fseek(fp, 0, SEEK_SET); } else { logger(LOG_INFO, "No more client, end of tranfers"); fclose(fp); return OK; } } else { logger(LOG_DEBUG, "End of transfer"); fclose(fp); return OK; } break; case S_ABORT: logger(LOG_DEBUG, "Aborting transfer"); fclose(fp); return ERR; default: fclose(fp); logger(LOG_ERR, "%s: %d: abnormal condition", __FILE__, __LINE__); return ERR; } } }
int find_own_ip (const char *requested_address) { const char *sep = " "; char last_if_name[80] = ""; int quality = -1; os_result res; int i; unsigned int nif; os_ifAttributes *ifs; int maxq_list[MAX_INTERFACES]; int maxq_count = 0; int maxq_strlen = 0; int selected_idx = -1; char addrbuf[INET6_ADDRSTRLEN_EXTENDED]; if ((ifs = os_malloc (MAX_INTERFACES * sizeof (*ifs))) == NULL) { NN_FATAL0 ("ddsi2: insufficient memory for enumerating network interfaces\n"); return 0; } nn_log (LC_CONFIG, "interfaces:"); if (config.useIpv6) res = os_sockQueryIPv6Interfaces (ifs, (os_uint32) MAX_INTERFACES, &nif); else res = os_sockQueryInterfaces (ifs, (os_uint32) MAX_INTERFACES, &nif); if (res != os_resultSuccess) { NN_ERROR1 ("os_sockQueryInterfaces: %d\n", (int) res); os_free (ifs); return 0; } gv.n_interfaces = 0; for (i = 0; i < (int) nif; i++, sep = ", ") { os_sockaddr_storage tmpip, tmpmask; char if_name[sizeof (last_if_name)]; int q = 0; os_strncpy (if_name, ifs[i].name, sizeof (if_name) - 1); if_name[sizeof (if_name) - 1] = 0; if (strcmp (if_name, last_if_name)) nn_log (LC_CONFIG, "%s%s", sep, if_name); os_strcpy (last_if_name, if_name); /* interface must be up */ if ((ifs[i].flags & IFF_UP) == 0) { nn_log (LC_CONFIG, " (interface down)"); continue; } tmpip = ifs[i].address; tmpmask = ifs[i].network_mask; sockaddr_to_string_no_port (addrbuf, &tmpip); nn_log (LC_CONFIG, " %s", addrbuf); if (ifs[i].flags & IFF_LOOPBACK) { /* Loopback device has the lowest priority of every interface available, because the other interfaces at least in principle allow communicating with other machines. */ q += 0; #if OS_SOCKET_HAS_IPV6 if (!(tmpip.ss_family == AF_INET6 && IN6_IS_ADDR_LINKLOCAL (&((os_sockaddr_in6 *) &tmpip)->sin6_addr))) q += 1; #endif } else { #if OS_SOCKET_HAS_IPV6 /* We accept link-local IPv6 addresses, but an interface with a link-local address will end up lower in the ordering than one with a global address. When forced to use a link-local address, we restrict ourselves to operating on that one interface only and assume any advertised (incoming) link-local address belongs to that interface. FIXME: this is wrong, and should be changed to tag addresses with the interface over which it was received. But that means proper multi-homing support and has quite an impact in various places, not least of which is the abstraction layer. */ if (!(tmpip.ss_family == AF_INET6 && IN6_IS_ADDR_LINKLOCAL (&((os_sockaddr_in6 *) &tmpip)->sin6_addr))) q += 5; #endif /* We strongly prefer a multicast capable interface, if that's not available anything that's not point-to-point, or else we hope IP routing will take care of the issues. */ if (ifs[i].flags & IFF_MULTICAST) q += 4; else if (!(ifs[i].flags & IFF_POINTOPOINT)) q += 3; else q += 2; } nn_log (LC_CONFIG, "(q%d)", q); if (q == quality) { maxq_list[maxq_count] = gv.n_interfaces; maxq_strlen += 2 + strlen (if_name); maxq_count++; } else if (q > quality) { maxq_list[0] = gv.n_interfaces; maxq_strlen += 2 + strlen (if_name); maxq_count = 1; quality = q; } gv.interfaces[gv.n_interfaces].addr = tmpip; gv.interfaces[gv.n_interfaces].netmask = tmpmask; gv.interfaces[gv.n_interfaces].mc_capable = ((ifs[i].flags & IFF_MULTICAST) != 0); gv.interfaces[gv.n_interfaces].point_to_point = ((ifs[i].flags & IFF_POINTOPOINT) != 0); gv.interfaces[gv.n_interfaces].if_index = ifs[i].interfaceIndexNo; gv.interfaces[gv.n_interfaces].name = os_strdup (if_name); gv.n_interfaces++; } nn_log (LC_CONFIG, "\n"); os_free (ifs); if (requested_address == NULL) { if (maxq_count > 1) { const int idx = maxq_list[0]; char *names; sockaddr_to_string_no_port (addrbuf, &gv.interfaces[idx].addr); if ((names = os_malloc (maxq_strlen + 1)) == NULL) NN_WARNING2 ("using network interface %s (%s) out of multiple candidates\n", gv.interfaces[idx].name, addrbuf); else { int p = 0; for (i = 0; i < maxq_count; i++) p += snprintf (names + p, maxq_strlen - p, ", %s", gv.interfaces[maxq_list[i]].name); NN_WARNING3 ("using network interface %s (%s) selected arbitrarily from: %s\n", gv.interfaces[idx].name, addrbuf, names + 2); os_free (names); } } if (maxq_count > 0) selected_idx = maxq_list[0]; else NN_ERROR0 ("failed to determine default own IP address\n"); } else { os_sockaddr_storage req; if (!os_sockaddrStringToAddress (config.networkAddressString, (os_sockaddr *) &req, !config.useIpv6)) { /* Presumably an interface name */ for (i = 0; i < gv.n_interfaces; i++) if (strcmp (gv.interfaces[i].name, config.networkAddressString) == 0) break; } else { /* Try an exact match on the address */ for (i = 0; i < gv.n_interfaces; i++) if (os_sockaddrIPAddressEqual ((os_sockaddr *) &gv.interfaces[i].addr, (os_sockaddr *) &req)) break; if (i == gv.n_interfaces && !config.useIpv6) { /* Try matching on network portion only, where the network portion is based on the netmask of the interface under consideration */ for (i = 0; i < gv.n_interfaces; i++) { os_sockaddr_storage req1 = req, ip1 = gv.interfaces[i].addr; assert (req1.ss_family == AF_INET); assert (ip1.ss_family == AF_INET); /* If the host portion of the requested address is non-zero, skip this interface */ if (((os_sockaddr_in *) &req1)->sin_addr.s_addr & ~((os_sockaddr_in *) &gv.interfaces[i].netmask)->sin_addr.s_addr) continue; ((os_sockaddr_in *) &req1)->sin_addr.s_addr &= ((os_sockaddr_in *) &gv.interfaces[i].netmask)->sin_addr.s_addr; ((os_sockaddr_in *) &ip1)->sin_addr.s_addr &= ((os_sockaddr_in *) &gv.interfaces[i].netmask)->sin_addr.s_addr; if (os_sockaddrIPAddressEqual ((os_sockaddr *) &ip1, (os_sockaddr *) &req1)) break; } } } if (i < gv.n_interfaces) selected_idx = i; else NN_ERROR1 ("%s: does not match an available interface\n", config.networkAddressString); } if (selected_idx < 0) return 0; else { gv.ownip = gv.interfaces[selected_idx].addr; sockaddr_set_port (&gv.ownip, 0); gv.selected_interface = selected_idx; gv.interfaceNo = gv.interfaces[selected_idx].if_index; #if OS_SOCKET_HAS_IPV6 if (config.useIpv6) { assert (gv.ownip.ss_family == AF_INET6); gv.ipv6_link_local = IN6_IS_ADDR_LINKLOCAL (&((os_sockaddr_in6 *) &gv.ownip)->sin6_addr) != 0; } else { gv.ipv6_link_local = 0; } #endif nn_log (LC_CONFIG, "selected interface: %s (index %u)\n", gv.interfaces[selected_idx].name, (unsigned) gv.interfaceNo); return 1; } }