Пример #1
0
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;
    
}
Пример #2
0
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;
}
Пример #3
0
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;
}
Пример #4
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);
	}
}
Пример #5
0
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;
    
}
Пример #6
0
/*
 * 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;
          }
     }
}
Пример #7
0
/*
 * 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;
          }
     }
}
Пример #8
0
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;
  }
}