build connection

neon 25.06.04 01:23

Yhteyden muodostaminen, IPv6 tuki ja paikallisen osoitteen bindaus

 Tekstiversio  Arvo: 2 (2 ääntä)  Äänestä: +  -
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/uio.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>

int connect_remote_host(const int socktype, const int addrfamily, const char *target_host, const int target_port, const char *localhost);


/**

        Esimerkki paikallisen osoitteen bindauksesta käytettävään
        sockettin ja sitten yhdistämisestä etääkoneeseen.
       
        Esimerkki toimii myös IPv6 esimerkkinä, koska kaikki on sekä
        IPv6 että IPv4 yhteensopivaa, riippuen kohdekoneen osoitteesta.
        Jos kohdekoneella on sekä IPv6 että IPv4 osoite ja ensin esimerkiksi
        yritetään yhdistää IPv6 osoitteeseen, joka epäonnistuu, yritetään
        vielä IPv4 osoitetta. Tai jos kohdekoneella on monta IP-osoitetta
        (multihomed) niin yritetään niin kauan kunnes yhteys onnistuu tai
        kaikki on kokeiltu läpi.
*/

/**
        @param const int socket  descriptor referencing the socket
        @param struct addinfo *hints    information about connection
        @param const char *hostname     local hostname to be binded
        @return int state              -1 if bind failed, 0 is successful

*/

int bind_local_addr(const int sock, struct addrinfo *hints, const char *hostname)
{
        struct addrinfo *res_all, *r;

        if(getaddrinfo(hostname, NULL, hints, &res_all)) { /* virhe */
                return -1;
        }
        r = res_all; /* Kopioidaan alkuperäisen pointterin osoite, jotta
                        kyseisen pointterin sisältö voidaan myöhemmin poistaa muistista. */

        while(r) {
                if(bind(sock, r->ai_addr, r->ai_addrlen) == -1) {
                        r = r->ai_next; /* kokeillaan seuraavaa osoitetta */
                } else {
                        /* bindaus onnistui */
                        freeaddrinfo(res_all);
                        return 0;
                }
        }
        freeaddrinfo(res_all);
        return -1;
}

/**
        @param const int socktype       the semantics of communication
        @param const int addrfamily     address family AF_INET, AF_INET6, AF_UNSPEC
        @param const int *target_host   hostname to be connected
        @param const int target_port    port at remotehost
        @param const char *hostname     local hostname to be binded
        @return int socket              descriptor referencing the socket
*/


int connect_remote_host(const int socktype, const int addrfamily, const char *target_host, const int target_port, const char *localhost)
{
        struct addrinfo hints, *res, *ressave;
        char port_buf[6];
        int sock = -1;

        memset(&hints, 0, sizeof(struct addrinfo));
        hints.ai_family = addrfamily;
        hints.ai_socktype = socktype;
       
        sprintf(port_buf,"%d",target_port); /* muutetaan portti stringiksi (liimaamalla) */

        /*
                Koska tarkoitus on tukea myös IPv6 osoitteita, tulee kohdejäjestelmän
                osoite selvittää ensin, jotta tiedetään kumpaa osoitetyyppiä käytetään
                paikallisen osoitteen bindauksessa.
        */

        if(getaddrinfo(target_host, port_buf, &hints, &res)) {
                return -1;
        }
        for (ressave = res; res; res = res->ai_next) {
                if((sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol)) < 0 )  {
                        continue;
                }
                /*
                        Yritetään bindata paikallinen osoite, jos asetettu arvoon NULL
                        saadaan wildcard-tyyppinen käytös.
                        Huomautettakoon, että bindausta yritetään kaikilla sopivilla paikallisilla osoiteilla,
                        mutta vain ensimmäistä toimivaa käytetään yhteysyrityksissä, eikä kaikkia.
                */

                if(bind_local_addr(sock,&hints,localhost) == -1) { /* virhe */
                        close(sock);
                        sock = -1;
                        continue;
                }
                /* yritetään yhdistää kohdejäjestelmään */
                if(connect(sock, res->ai_addr, res->ai_addrlen) < 0) { /* virhe */
                        close(sock);
                        sock = -1;
                        continue;
                }
                /*
                        Jos silmukka on suoritettu loppuun asti ei virheitä ole esiintynyt.
                        Poistutaan silmukasta.
                */

                break;
        }
        freeaddrinfo(ressave);
        return sock;
}