summaryrefslogtreecommitdiffstats
path: root/lib/webrick/httpauth/authenticator.rb
blob: fe2dbf4e0c5b0aef126b2da3e5b965e34e1f6f19 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
#
# httpauth/authenticator.rb -- Authenticator mix-in module.
#
# Author: IPR -- Internet Programming with Ruby -- writers
# Copyright (c) 2003 Internet Programming with Ruby writers. All rights
# reserved.
#
# $IPR: authenticator.rb,v 1.3 2003/02/20 07:15:47 gotoyuzo Exp $

module WEBrick
  module HTTPAuth
    module Authenticator
      RequestField      = "Authorization"
      ResponseField     = "WWW-Authenticate"
      ResponseInfoField = "Authentication-Info"
      AuthException     = HTTPStatus::Unauthorized
      AuthScheme        = nil # must override by the derived class

      attr_reader :realm, :userdb, :logger

      private

      def check_init(config)
        [:UserDB, :Realm].each{|sym|
          unless config[sym]
            raise ArgumentError, "Argument #{sym.inspect} missing."
          end
        } 
        @realm     = config[:Realm]
        @userdb    = config[:UserDB]
        @logger    = config[:Logger] || Log::new($stderr)
        @reload_db = config[:AutoReloadUserDB]
        @request_field   = self::class::RequestField
        @response_field  = self::class::ResponseField
        @resp_info_field = self::class::ResponseInfoField
        @auth_exception  = self::class::AuthException
        @auth_scheme     = self::class::AuthScheme
      end

      def check_scheme(req)
        unless credentials = req[@request_field]
          error("no credentials in the request.")
          return nil 
        end  
        unless match = /^#{@auth_scheme}\s+/.match(credentials)
          error("invalid scheme in %s.", credentials)
          info("%s: %s", @request_field, credentials) if $DEBUG
          return nil
        end
        return match.post_match
      end

      def log(meth, fmt, *args)
        msg = format("%s %s: ", @auth_scheme, @realm)
        msg << fmt % args
        @logger.send(meth, msg)
      end

      def error(fmt, *args)
        if @logger.error?
          log(:error, fmt, *args)
        end
      end                             

      def info(fmt, *args)
        if @logger.info?
          log(:info, fmt, *args)
        end
      end
    end

    module ProxyAuthenticator
      RequestField  = "Proxy-Authorization"
      ResponseField = "Proxy-Authenticate"
      InfoField     = "Proxy-Authentication-Info"
      AuthException = HTTPStatus::ProxyAuthenticationRequired
    end
  end
end
09'>309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942
/*
   Copyright (c) 2014 Red Hat, Inc. <http://www.redhat.com>
   This file is part of GlusterFS.

   This file is licensed to you under your choice of the GNU Lesser
   General Public License, version 3 or any later version (LGPLv3 or
   later), or the GNU General Public License, version 2 (GPLv2), in all
   cases as published by the Free Software Foundation.
*/

#include "glusterd-peer-utils.h"
#include "glusterd-store.h"
#include "glusterd-server-quorum.h"
#include "common-utils.h"

void
glusterd_peerinfo_destroy (struct rcu_head *head)
{
        int32_t                   ret      = -1;
        glusterd_peerinfo_t      *peerinfo = NULL;
        glusterd_peer_hostname_t *hostname = NULL;
        glusterd_peer_hostname_t *tmp      = NULL;

        /* This works as rcu_head is the first member of gd_rcu_head */
        peerinfo = caa_container_of ((gd_rcu_head *)head, glusterd_peerinfo_t,
                                      rcu_head);

        /* Set THIS to the saved this. Needed by some functions below */
        THIS = peerinfo->rcu_head.this;

        CDS_INIT_LIST_HEAD (&peerinfo->uuid_list);

        ret = glusterd_store_delete_peerinfo (peerinfo);
        if (ret) {
                gf_log ("glusterd", GF_LOG_ERROR, "Deleting peer info failed");
        }

        GF_FREE (peerinfo->hostname);
        peerinfo->hostname = NULL;

        cds_list_for_each_entry_safe (hostname, tmp, &peerinfo->hostnames,
                                      hostname_list) {
                glusterd_peer_hostname_free (hostname);
        }

        glusterd_sm_tr_log_delete (&peerinfo->sm_log);
        pthread_mutex_destroy (&peerinfo->delete_lock);
        GF_FREE (peerinfo);

        peerinfo = NULL;

        return;
}

int32_t
glusterd_peerinfo_cleanup (glusterd_peerinfo_t *peerinfo)
{
        GF_ASSERT (peerinfo);
        glusterd_peerctx_t      *peerctx = NULL;
        gf_boolean_t            quorum_action = _gf_false;
        glusterd_conf_t         *priv = THIS->private;

        if (pthread_mutex_trylock (&peerinfo->delete_lock)) {
                /* Someone else is already deleting the peer, so give up */
                return 0;
        }

        if (peerinfo->quorum_contrib != QUORUM_NONE)
                quorum_action = _gf_true;
        if (peerinfo->rpc) {
                peerinfo->rpc = glusterd_rpc_clnt_unref (priv, peerinfo->rpc);
                peerinfo->rpc = NULL;
        }

        cds_list_del_rcu (&peerinfo->uuid_list);
        /* Saving THIS, as it is needed by the callback function */
        peerinfo->rcu_head.this = THIS;
        call_rcu (&peerinfo->rcu_head.head, glusterd_peerinfo_destroy);

        if (quorum_action)
                glusterd_do_quorum_action ();
        return 0;
}

/* glusterd_peerinfo_find_by_hostname searches for a peer which matches the
 * hostname @hoststr and if found returns the pointer to peerinfo object.
 * Returns NULL otherwise.
 *
 * It first attempts a quick search by string matching @hoststr. If that fails,
 * it'll attempt a more thorough match by resolving the addresses and matching
 * the resolved addrinfos.
 */
glusterd_peerinfo_t *
glusterd_peerinfo_find_by_hostname (const char *hoststr)
{
        int                  ret      = -1;
        struct addrinfo     *addr     = NULL;
        struct addrinfo     *p        = NULL;
        xlator_t            *this     = NULL;
        glusterd_peerinfo_t *peerinfo = NULL;


        this = THIS;
        GF_ASSERT (hoststr);

        peerinfo = NULL;

        peerinfo = gd_peerinfo_find_from_hostname (hoststr);
        if (peerinfo)
                return peerinfo;

        ret = getaddrinfo (hoststr, NULL, NULL, &addr);
        if (ret != 0) {
                gf_log (this->name, GF_LOG_ERROR,
                        "error in getaddrinfo: %s\n",
                        gai_strerror(ret));
                goto out;
        }

        for (p = addr; p != NULL; p = p->ai_next) {
                peerinfo = gd_peerinfo_find_from_addrinfo (p);
                if (peerinfo) {
                        freeaddrinfo (addr);
                        return peerinfo;
                }
        }

out:
        gf_log (this->name, GF_LOG_DEBUG, "Unable to find friend: %s", hoststr);
        if (addr)
                freeaddrinfo (addr);
        return NULL;
}

int
glusterd_hostname_to_uuid (char *hostname, uuid_t uuid)
{
        GF_ASSERT (hostname);
        GF_ASSERT (uuid);

        glusterd_peerinfo_t     *peerinfo = NULL;
        glusterd_conf_t         *priv = NULL;
        int                     ret = -1;
        xlator_t                *this = NULL;

        this = THIS;
        GF_ASSERT (this);
        priv = this->private;
        GF_ASSERT (priv);

        peerinfo = glusterd_peerinfo_find_by_hostname (hostname);
        if (peerinfo) {
                ret = 0;
                gf_uuid_copy (uuid, peerinfo->uuid);
        } else {
                if (gf_is_local_addr (hostname)) {
                        gf_uuid_copy (uuid, MY_UUID);
                        ret = 0;
                } else {
                        ret = -1;
                }
        }

        gf_log (this->name, GF_LOG_DEBUG, "returning %d", ret);
        return ret;
}

/* glusterd_peerinfo_find_by_uuid searches for a peer which matches the
 * uuid @uuid and if found returns the pointer to peerinfo object.
 * Returns NULL otherwise.
 */
glusterd_peerinfo_t *
glusterd_peerinfo_find_by_uuid (uuid_t uuid)
{
        glusterd_conf_t         *priv = NULL;
        glusterd_peerinfo_t     *entry = NULL;
        glusterd_peerinfo_t     *found = NULL;
        xlator_t                *this = NULL;

        this = THIS;
        GF_ASSERT (this);

        priv    = this->private;

        GF_ASSERT (priv);

        if (gf_uuid_is_null (uuid))
                return NULL;

        rcu_read_lock ();
        cds_list_for_each_entry_rcu (entry, &priv->peers, uuid_list) {
                if (!gf_uuid_compare (entry->uuid, uuid)) {

                        gf_log (this->name, GF_LOG_DEBUG,
                                 "Friend found... state: %s",
                        glusterd_friend_sm_state_name_get (entry->state.state));
                        found = entry; /* Probably should be rcu_dereferenced */
                        break;
                }
        }
        rcu_read_unlock ();

        if (!found)
                gf_log (this->name, GF_LOG_DEBUG,
                        "Friend with uuid: %s, not found", uuid_utoa (uuid));
        return found;
}

/* glusterd_peerinfo_find will search for a peer matching either @uuid or
 * @hostname and return a pointer to the peerinfo object
 * Returns NULL otherwise.
 */
glusterd_peerinfo_t *
glusterd_peerinfo_find (uuid_t uuid, const char *hostname)
{
        glusterd_peerinfo_t *peerinfo = NULL;
        xlator_t            *this     = NULL;

        this = THIS;
        GF_ASSERT (this);


        if (uuid) {
                peerinfo = glusterd_peerinfo_find_by_uuid (uuid);

                if (peerinfo) {
                        return peerinfo;
                } else {
                        gf_log (this->name, GF_LOG_DEBUG,
                                 "Unable to find peer by uuid: %s",
                                 uuid_utoa (uuid));
                }

        }

        if (hostname) {
                peerinfo = glusterd_peerinfo_find_by_hostname (hostname);

                if (peerinfo) {
                        return peerinfo;
                } else {
                        gf_log (this->name, GF_LOG_DEBUG,
                                "Unable to find hostname: %s", hostname);
                }
        }
        return NULL;
}

/* glusterd_peerinfo_new will create a new peerinfo object and set it's members
 * values using the passed parameters.
 * @hostname is added as the first entry in peerinfo->hostnames list and also
 * set to peerinfo->hostname.
 * It returns a pointer to peerinfo object if successful and returns NULL
 * otherwise. The caller should take care of freeing the created peerinfo
 * object.
 */
glusterd_peerinfo_t *
glusterd_peerinfo_new (glusterd_friend_sm_state_t state, uuid_t *uuid,
                       const char *hostname, int port)
{
        glusterd_peerinfo_t      *new_peer = NULL;
        int                      ret = -1;

        new_peer = GF_CALLOC (1, sizeof (*new_peer), gf_gld_mt_peerinfo_t);
        if (!new_peer)
                goto out;

        CDS_INIT_LIST_HEAD (&new_peer->uuid_list);

        new_peer->state.state = state;

        CDS_INIT_LIST_HEAD (&new_peer->hostnames);
        if (hostname) {
                ret = gd_add_address_to_peer (new_peer, hostname);
                if (ret)
                        goto out;
                /* Also set it to peerinfo->hostname. Doing this as we use
                 * peerinfo->hostname in a lot of places and is really hard to
                 * get everything right
                 */
                new_peer->hostname = gf_strdup (hostname);
        }

        if (uuid) {
                gf_uuid_copy (new_peer->uuid, *uuid);
        }

        ret = glusterd_sm_tr_log_init (&new_peer->sm_log,
                                       glusterd_friend_sm_state_name_get,
                                       glusterd_friend_sm_event_name_get,
                                       GLUSTERD_TR_LOG_SIZE);
        if (ret)
                goto out;

        if (new_peer->state.state == GD_FRIEND_STATE_BEFRIENDED)
                new_peer->quorum_contrib = QUORUM_WAITING;
        new_peer->port = port;

        pthread_mutex_init (&new_peer->delete_lock, NULL);
out:
        if (ret && new_peer) {
                glusterd_peerinfo_cleanup (new_peer);
                new_peer = NULL;
        }
        return new_peer;
}

/* Check if the all peers are connected and befriended, except the peer
 * specified (the peer being detached)
 */
gf_boolean_t
glusterd_chk_peers_connected_befriended (uuid_t skip_uuid)
{
        gf_boolean_t            ret = _gf_true;
        glusterd_peerinfo_t     *peerinfo = NULL;
        glusterd_conf_t         *priv = NULL;

        priv= THIS->private;
        GF_ASSERT (priv);

        rcu_read_lock ();
        cds_list_for_each_entry_rcu (peerinfo, &priv->peers, uuid_list) {

                if (!gf_uuid_is_null (skip_uuid) && !gf_uuid_compare (skip_uuid,
                                                           peerinfo->uuid))
                        continue;

                if ((GD_FRIEND_STATE_BEFRIENDED != peerinfo->state.state)
                    || !(peerinfo->connected)) {
                        ret = _gf_false;
                        break;
                }
        }
        rcu_read_unlock ();

        gf_log (THIS->name, GF_LOG_DEBUG, "Returning %s",
                (ret?"TRUE":"FALSE"));
        return ret;
}

/* Return hostname for given uuid if it exists
 * else return NULL
 */
char *
glusterd_uuid_to_hostname (uuid_t uuid)
{
        char                    *hostname = NULL;
        glusterd_conf_t         *priv = NULL;
        glusterd_peerinfo_t     *entry = NULL;

        priv = THIS->private;
        GF_ASSERT (priv);

        if (!gf_uuid_compare (MY_UUID, uuid)) {
                hostname = gf_strdup ("localhost");
        }
        rcu_read_lock ();
        if (!cds_list_empty (&priv->peers)) {
                cds_list_for_each_entry_rcu (entry, &priv->peers, uuid_list) {
                        if (!gf_uuid_compare (entry->uuid, uuid)) {
                                hostname = gf_strdup (entry->hostname);
                                break;
                        }
                }
        }
        rcu_read_unlock ();

        return hostname;
}

char*
gd_peer_uuid_str (glusterd_peerinfo_t *peerinfo)
{
        if ((peerinfo == NULL) || gf_uuid_is_null (peerinfo->uuid))
                return NULL;

        if (peerinfo->uuid_str[0] == '\0')
                uuid_utoa_r (peerinfo->uuid, peerinfo->uuid_str);

        return peerinfo->uuid_str;
}

gf_boolean_t
glusterd_are_vol_all_peers_up (glusterd_volinfo_t *volinfo,
                               struct cds_list_head *peers,
                               char **down_peerstr)
{
        glusterd_peerinfo_t   *peerinfo  = NULL;
        glusterd_brickinfo_t  *brickinfo = NULL;
        gf_boolean_t           ret       = _gf_false;

        cds_list_for_each_entry (brickinfo, &volinfo->bricks, brick_list) {
                if (!gf_uuid_compare (brickinfo->uuid, MY_UUID))
                        continue;

                rcu_read_lock ();
                cds_list_for_each_entry_rcu (peerinfo, peers, uuid_list) {
                        if (gf_uuid_compare (peerinfo->uuid, brickinfo->uuid))
                                continue;

                        /*Found peer who owns the brick, return false
                         * if peer is not connected or not friend */
                        if (!(peerinfo->connected) ||
                           (peerinfo->state.state !=
                             GD_FRIEND_STATE_BEFRIENDED)) {
                                *down_peerstr = gf_strdup (peerinfo->hostname);
                                gf_log ("", GF_LOG_DEBUG, "Peer %s is down. ",
                                        peerinfo->hostname);
                                rcu_read_unlock ();
                                goto out;
                        }
                }
                rcu_read_unlock ();
        }

        ret = _gf_true;
out:
        gf_log ("glusterd", GF_LOG_DEBUG, "Returning %d", ret);
        return ret;
}

int32_t
glusterd_peer_hostname_new (const char *hostname,
                            glusterd_peer_hostname_t **name)
{
        glusterd_peer_hostname_t        *peer_hostname = NULL;
        int32_t                         ret = -1;

        GF_ASSERT (hostname);
        GF_ASSERT (name);

        peer_hostname = GF_CALLOC (1, sizeof (*peer_hostname),
                                   gf_gld_mt_peer_hostname_t);

        if (!peer_hostname)
                goto out;

        peer_hostname->hostname = gf_strdup (hostname);
        CDS_INIT_LIST_HEAD (&peer_hostname->hostname_list);

        *name = peer_hostname;
        ret = 0;

out:
        gf_log ("glusterd", GF_LOG_DEBUG, "Returning %d", ret);
        return ret;
}

void
glusterd_peer_hostname_free (glusterd_peer_hostname_t *name)
{
        if (!name)
                return;

        cds_list_del_init (&name->hostname_list);

        GF_FREE (name->hostname);
        name->hostname = NULL;

        GF_FREE (name);

        return;
}

gf_boolean_t
gd_peer_has_address (glusterd_peerinfo_t *peerinfo, const char *address)
{
        gf_boolean_t ret = _gf_false;
        glusterd_peer_hostname_t *hostname = NULL;

        GF_VALIDATE_OR_GOTO ("glusterd", (peerinfo != NULL), out);
        GF_VALIDATE_OR_GOTO ("glusterd", (address != NULL), out);

        cds_list_for_each_entry (hostname, &peerinfo->hostnames,
                                 hostname_list) {
                if (strcmp (hostname->hostname, address) == 0) {
                        ret = _gf_true;
                        break;
                }
        }

out:
        return ret;
}

int
gd_add_address_to_peer (glusterd_peerinfo_t *peerinfo, const char *address)
{

        int ret = -1;
        glusterd_peer_hostname_t *hostname = NULL;

        GF_VALIDATE_OR_GOTO ("glusterd", (peerinfo != NULL), out);
        GF_VALIDATE_OR_GOTO ("glusterd", (address != NULL), out);

        if (gd_peer_has_address (peerinfo, address)) {
                ret = 0;
                goto out;
        }

        ret = glusterd_peer_hostname_new (address, &hostname);
        if (ret)
                goto out;

        cds_list_add_tail_rcu (&hostname->hostname_list, &peerinfo->hostnames);

        ret = 0;
out:
        return ret;
}

/* gd_add_friend_to_dict() adds details of @friend into @dict with the given
 * @prefix. All the parameters are compulsory.
 *
 * The complete address list is added to the dict only if the cluster op-version
 * is >= GD_OP_VERSION_3_6_0
 */
int
gd_add_friend_to_dict (glusterd_peerinfo_t *friend, dict_t *dict,
                       const char *prefix)
{
        int                       ret      = -1;
        xlator_t                 *this     = NULL;
        glusterd_conf_t          *conf     = NULL;
        char                      key[100] = {0,};
        glusterd_peer_hostname_t *address  = NULL;
        int                       count    = 0;

        this = THIS;
        GF_VALIDATE_OR_GOTO ("glusterd", (this != NULL), out);

        conf = this->private;
        GF_VALIDATE_OR_GOTO (this->name, (conf != NULL), out);

        GF_VALIDATE_OR_GOTO (this->name, (friend != NULL), out);
        GF_VALIDATE_OR_GOTO (this->name, (dict != NULL), out);
        GF_VALIDATE_OR_GOTO (this->name, (prefix != NULL), out);

        snprintf (key, sizeof (key), "%s.uuid", prefix);
        ret = dict_set_dynstr_with_alloc (dict, key, uuid_utoa (friend->uuid));
        if (ret) {
                gf_log (this->name, GF_LOG_ERROR,
                        "Failed to set key %s in dict", key);
                goto out;
        }

        /* Setting the first hostname from the list with this key for backward
         * compatibility
         */
        memset (key, 0, sizeof (key));
        snprintf (key, sizeof (key), "%s.hostname", prefix);
        address = cds_list_entry (&friend->hostnames, glusterd_peer_hostname_t,
                                  hostname_list);
        if (!address) {
                ret = -1;
                gf_log (this->name, GF_LOG_ERROR, "Could not retrieve first "
                        "address for peer");
                goto out;
        }
        ret = dict_set_dynstr_with_alloc (dict, key, address->hostname);
        if (ret) {
                gf_log (this->name, GF_LOG_ERROR,
                        "Failed to set key %s in dict", key);
                goto out;
        }

        if (conf->op_version < GD_OP_VERSION_3_6_0) {
                ret = 0;
                goto out;
        }

        address = NULL;
        count = 0;
        cds_list_for_each_entry (address, &friend->hostnames, hostname_list) {
                GF_VALIDATE_OR_GOTO (this->name, (address != NULL), out);

                memset (key, 0, sizeof (key));
                snprintf (key, sizeof (key), "%s.hostname%d", prefix, count);
                ret = dict_set_dynstr_with_alloc (dict, key, address->hostname);
                if (ret) {
                        gf_log (this->name, GF_LOG_ERROR,
                                        "Failed to set key %s in dict", key);
                        goto out;
                }
                count++;
        }
        memset (key, 0, sizeof (key));
        snprintf (key, sizeof (key), "%s.address-count", prefix);
        ret = dict_set_int32 (dict, key, count);
        if (ret)
                gf_log (this->name, GF_LOG_ERROR,
                        "Failed to set key %s in dict", key);

out:
        gf_log (this ? this->name : "glusterd", GF_LOG_DEBUG, "Returning %d",
                ret);
        return ret;
}

/* gd_peerinfo_find_from_hostname iterates over all the addresses saved for each
 * peer and matches it to @hoststr.
 * Returns the matched peer if found else returns NULL
 */
glusterd_peerinfo_t *
gd_peerinfo_find_from_hostname (const char *hoststr)
{
        xlator_t                 *this    = NULL;
        glusterd_conf_t          *priv    = NULL;
        glusterd_peerinfo_t      *peer    = NULL;
        glusterd_peerinfo_t      *found   = NULL;
        glusterd_peer_hostname_t *tmphost = NULL;

        this = THIS;
        GF_ASSERT (this != NULL);
        priv = this->private;
        GF_VALIDATE_OR_GOTO (this->name, (priv != NULL), out);

        GF_VALIDATE_OR_GOTO (this->name, (hoststr != NULL), out);

        rcu_read_lock ();
        cds_list_for_each_entry_rcu (peer, &priv->peers, uuid_list) {
                cds_list_for_each_entry_rcu (tmphost, &peer->hostnames,
                                             hostname_list) {
                        if (!strncasecmp (tmphost->hostname, hoststr, 1024)) {
                                gf_log (this->name, GF_LOG_DEBUG,
                                        "Friend %s found.. state: %d",
                                        tmphost->hostname, peer->state.state);
                                found = peer; /* Probably needs to be
                                                 dereferenced*/
                                goto unlock;
                        }
                }
        }
unlock:
        rcu_read_unlock ();
out:
        return found;
}

/* gd_peerinfo_find_from_addrinfo iterates over all the addresses saved for each
 * peer, resolves them and compares them to @addr.
 *
 *
 * NOTE: As getaddrinfo is a blocking call and is being performed multiple times
 * in this function, it could lead to the calling thread to be blocked for
 * significant amounts of time.
 *
 * Returns the matched peer if found else returns NULL
 */
glusterd_peerinfo_t *
gd_peerinfo_find_from_addrinfo (const struct addrinfo *addr)
{
        xlator_t                 *this    = NULL;
        glusterd_conf_t          *conf    = NULL;
        glusterd_peerinfo_t      *peer    = NULL;
        glusterd_peerinfo_t      *found   = NULL;
        glusterd_peer_hostname_t *address = NULL;
        int                       ret     = 0;
        struct addrinfo          *paddr   = NULL;
        struct addrinfo          *tmp     = NULL;

        this = THIS;
        GF_ASSERT (this != NULL);
        conf = this->private;
        GF_VALIDATE_OR_GOTO (this->name, (conf != NULL), out);

        GF_VALIDATE_OR_GOTO (this->name, (addr != NULL), out);

        rcu_read_lock ();
        cds_list_for_each_entry_rcu (peer, &conf->peers, uuid_list) {
                cds_list_for_each_entry_rcu (address, &peer->hostnames,
                                             hostname_list) {
                        /* TODO: Cache the resolved addrinfos to improve
                         * performance
                         */
                        ret = getaddrinfo (address->hostname, NULL, NULL,
                                           &paddr);
                        if (ret) {
                                /* Don't fail if getaddrinfo fails, continue
                                 * onto the next address
                                 */
                                gf_log (this->name, GF_LOG_TRACE,
                                        "getaddrinfo for %s failed (%s)",
                                        address->hostname, gai_strerror (ret));
                                ret = 0;
                                continue;
                        }

                        for (tmp = paddr; tmp != NULL; tmp = tmp->ai_next) {
                                if (gf_compare_sockaddr (addr->ai_addr,
                                                         tmp->ai_addr)) {
                                        found = peer; /* (de)referenced? */
                                        break;
                                }
                        }

                        freeaddrinfo (paddr);
                        if (found)
                                goto unlock;
                }
        }
unlock:
        rcu_read_unlock ();
out:
        return found;
}

/* gd_update_peerinfo_from_dict will update the hostnames for @peerinfo from
 * peer details with @prefix in @dict.
 * Returns 0 on success and -1 on failure.
 */
int
gd_update_peerinfo_from_dict (glusterd_peerinfo_t *peerinfo, dict_t *dict,
                              const char *prefix)
{
        int                  ret      = -1;
        xlator_t            *this     = NULL;
        glusterd_conf_t     *conf     = NULL;
        char                 key[100] = {0,};
        char                *hostname = NULL;
        int                  count    = 0;
        int                  i        = 0;

        this = THIS;
        GF_ASSERT (this != NULL);

        conf = this->private;
        GF_VALIDATE_OR_GOTO (this->name, (conf != NULL), out);

        GF_VALIDATE_OR_GOTO (this->name, (peerinfo != NULL), out);
        GF_VALIDATE_OR_GOTO (this->name, (dict != NULL), out);
        GF_VALIDATE_OR_GOTO (this->name, (prefix != NULL), out);

        memset (key, 0, sizeof (key));
        snprintf (key, sizeof (key), "%s.hostname", prefix);
        ret = dict_get_str (dict, key, &hostname);
        if (ret) {
                gf_log (this->name, GF_LOG_ERROR, "Key %s not present in "
                        "dictionary", key);
                goto out;
        }
        ret = gd_add_address_to_peer (peerinfo, hostname);
        if (ret) {
                gf_log (this->name, GF_LOG_ERROR,
                        "Could not add address to peer");
                goto out;
        }
        /* Also set peerinfo->hostname to the first address */
        if (peerinfo->hostname != NULL)
                GF_FREE (peerinfo->hostname);
        peerinfo->hostname = gf_strdup (hostname);

        if (conf->op_version < GD_OP_VERSION_3_6_0) {
                ret = 0;
                goto out;
        }

        memset (key, 0, sizeof (key));
        snprintf (key, sizeof (key), "%s.address-count", prefix);
        ret = dict_get_int32 (dict, key, &count);
        if (ret) {
                gf_log (this->name, GF_LOG_ERROR, "Key %s not present in "
                        "dictionary", key);
                goto out;
        }
        hostname = NULL;
        for (i = 0; i < count; i++) {
                memset (key, 0, sizeof (key));
                snprintf (key, sizeof (key), "%s.hostname%d",prefix, i);
                ret = dict_get_str (dict, key, &hostname);
                if (ret) {
                        gf_log (this->name, GF_LOG_ERROR, "Key %s not present "
                                "in dictionary", key);
                        goto out;
                }
                ret = gd_add_address_to_peer (peerinfo, hostname);
                if (ret) {
                        gf_log (this->name, GF_LOG_ERROR,
                                "Could not add address to peer");
                        goto out;
                }

                hostname = NULL;
        }

out:
        gf_log (this->name, GF_LOG_DEBUG, "Returning %d", ret);
        return ret;
}

/* gd_peerinfo_from_dict creates a peerinfo object from details of peer with
 * @prefix in @dict.
 * Returns a pointer to the created peerinfo object on success, and NULL on
 * failure.
 */
glusterd_peerinfo_t *
gd_peerinfo_from_dict (dict_t *dict, const char *prefix)
{
        int                  ret      = -1;
        xlator_t            *this     = NULL;
        glusterd_conf_t     *conf     = NULL;
        glusterd_peerinfo_t *new_peer = NULL;
        char                 key[100] = {0,};
        char                *uuid_str = NULL;

        this = THIS;
        GF_VALIDATE_OR_GOTO ("glusterd", (this != NULL), out);

        conf = this->private;
        GF_VALIDATE_OR_GOTO (this->name, (conf != NULL), out);

        GF_VALIDATE_OR_GOTO (this->name, (dict != NULL), out);
        GF_VALIDATE_OR_GOTO (this->name, (prefix != NULL), out);

        new_peer = glusterd_peerinfo_new (GD_FRIEND_STATE_DEFAULT, NULL, NULL,
                                          0);
        if (new_peer == NULL) {
                ret = -1;
                gf_log (this->name, GF_LOG_ERROR, "Could not create peerinfo "
                        "object");
                goto out;
        }

        snprintf (key, sizeof (key), "%s.uuid", prefix);
        ret = dict_get_str (dict, key, &uuid_str);
        if (ret) {
                gf_log (this->name, GF_LOG_ERROR, "Key %s not present in "
                        "dictionary", key);
                goto out;
        }
        gf_uuid_parse (uuid_str, new_peer->uuid);

        ret = gd_update_peerinfo_from_dict (new_peer, dict, prefix);

out:
        if ((ret != 0) && (new_peer != NULL)) {
                glusterd_peerinfo_cleanup (new_peer);
                new_peer = NULL;
        }

        return new_peer;
}

int
gd_add_peer_hostnames_to_dict (glusterd_peerinfo_t *peerinfo, dict_t *dict,
                               const char *prefix)
{
        int                       ret      = -1;
        xlator_t                 *this     = NULL;
        glusterd_conf_t          *conf     = NULL;
        char                      key[256] = {0,};
        glusterd_peer_hostname_t *addr     = NULL;
        int                       count    = 0;

        this = THIS;
        GF_ASSERT (this != NULL);

        conf = this->private;
        GF_VALIDATE_OR_GOTO (this->name, (conf != NULL), out);

        if (conf->op_version < GD_OP_VERSION_3_6_0) {
                ret = 0;
                goto out;
        }

        GF_VALIDATE_OR_GOTO (this->name, (peerinfo != NULL), out);
        GF_VALIDATE_OR_GOTO (this->name, (dict != NULL), out);
        GF_VALIDATE_OR_GOTO (this->name, (prefix != NULL), out);

        cds_list_for_each_entry (addr, &peerinfo->hostnames, hostname_list) {
                memset (key, 0, sizeof (key));
                snprintf (key, sizeof (key), "%s.hostname%d", prefix, count);
                ret = dict_set_dynstr_with_alloc (dict, key, addr->hostname);
                if (ret)
                        goto out;
                count++;
        }

        memset (key, 0, sizeof (key));
        snprintf (key, sizeof (key), "%s.hostname_count", prefix);
        ret = dict_set_int32 (dict, key, count);

out:
        return ret;
}

int
gd_add_peer_detail_to_dict (glusterd_peerinfo_t *peerinfo, dict_t *friends,
                            int count)
{

        int             ret = -1;
        char            key[256] = {0, };
        char           *peer_uuid_str = NULL;

        GF_ASSERT (peerinfo);
        GF_ASSERT (friends);

        snprintf (key, sizeof (key), "friend%d.uuid", count);
        peer_uuid_str = gd_peer_uuid_str (peerinfo);
        ret = dict_set_str (friends, key, peer_uuid_str);
        if (ret)
                goto out;

        memset (key, 0, sizeof (key));
        snprintf (key, sizeof (key), "friend%d.hostname", count);
        ret = dict_set_str (friends, key, peerinfo->hostname);
        if (ret)
                goto out;

        memset (key, 0, sizeof (key));
        snprintf (key, sizeof (key), "friend%d.port", count);
        ret = dict_set_int32 (friends, key, peerinfo->port);
        if (ret)
                goto out;

        memset (key, 0, sizeof (key));
        snprintf (key, sizeof (key), "friend%d.stateId", count);
        ret = dict_set_int32 (friends, key, peerinfo->state.state);
        if (ret)
                goto out;

        memset (key, 0, sizeof (key));
        snprintf (key, sizeof (key), "friend%d.state", count);
        ret = dict_set_str (friends, key,
                    glusterd_friend_sm_state_name_get(peerinfo->state.state));
        if (ret)
                goto out;

        memset (key, 0, sizeof (key));
        snprintf (key, sizeof (key), "friend%d.connected", count);
        ret = dict_set_int32 (friends, key, (int32_t)peerinfo->connected);
        if (ret)
                goto out;

        memset (key, 0, sizeof (key));
        snprintf (key, sizeof (key), "friend%d", count);
        ret = gd_add_peer_hostnames_to_dict (peerinfo, friends, key);

out:
        return ret;
}