Implementing IPv6 ... - Mailing list pgsql-odbc
From | Hans-Jürgen Schönig |
---|---|
Subject | Implementing IPv6 ... |
Date | |
Msg-id | 42FBAF67.6010305@cybertec.at Whole thread Raw |
List | pgsql-odbc |
Folks, This patch implements IPv6 for some widespread platforms. It has been tested on Linux. Maybe some people will have the time to test some other platforms. Best regards, Hans -- Cybertec Geschwinde & Schönig GmbH Schöngrabern 134; A-2020 Hollabrunn Tel: +43/1/205 10 35 / 340 www.postgresql.at, www.cybertec.at This patch implements IPv6 for PostgreSQL ODBC. The patch is __without__ ./configure changes, there's new configure.in stuff only. It means you have to call "autoconf" after the patch command. patch -p1 < psqlodbc-08.00.0102-ipv6.patch autoconf ./configure --enable-pthreads --with-unixodbc make /etc/odbc.ini (IPv6 localhost) [PostgreSQL] Description = Test to Postgres Driver = /usr/lib/psqlodbc.so Database = test Servername = ::ffff:127.0.0.1 UserName = you Password = -- 10-Aug-2005 Hans-Jurgen Schonig <hs@cybertec.at> diff -B -u -r --new-file psqlodbc-08.00.0102.old/config.h.in psqlodbc-08.00.0102.new/config.h.in --- psqlodbc-08.00.0102.old/config.h.in 2005-08-02 08:58:09.000000000 +0200 +++ psqlodbc-08.00.0102.new/config.h.in 2005-08-10 14:59:14.000000000 +0200 @@ -36,6 +36,15 @@ /* Define to 1 if you have the `socket' library (-lsocket). */ #undef HAVE_LIBSOCKET +/* Define to 1 if you have the `getaddrinfo' function. */ +#undef HAVE_GETADDRINFO + +/* Define to 1 if you have support for IPv6. */ +#undef HAVE_IPV6 + +/* Define to 1 if you have support for sockaddr absraction */ +#undef HAVE_STRUCT_SOCKADDR_STORAGE + /* Define to 1 if you have the <locale.h> header file. */ #undef HAVE_LOCALE_H diff -B -u -r --new-file psqlodbc-08.00.0102.old/configure.ac psqlodbc-08.00.0102.new/configure.ac --- psqlodbc-08.00.0102.old/configure.ac 2005-08-02 09:38:03.000000000 +0200 +++ psqlodbc-08.00.0102.new/configure.ac 2005-08-10 15:06:21.000000000 +0200 @@ -44,6 +44,24 @@ AC_DEFINE(WITH_IODBC, 1, [Define to 1 to build with iODBC support]) fi +# +# IPv6 and modern network address translation +# +AC_CHECK_FUNCS(getaddrinfo) +AC_CHECK_TYPE([struct sockaddr_storage], + [AC_DEFINE(HAVE_STRUCT_SOCKADDR_STORAGE, 1, [Define to 1 if you have struct sockaddr_storage.])], + [], +[$ac_includes_default +#include <netinet/in.h>]) + +HAVE_IPV6=no +AC_CHECK_TYPE([struct sockaddr_in6], + [AC_DEFINE(HAVE_IPV6, 1, [Define to 1 if you have support for IPv6.])], + [HAVE_IPV6=no], +[$ac_includes_default +#include <netinet/in.h>]) +AC_SUBST(HAVE_IPV6) + # # Default odbc version number (--with-odbcver), default 0x0300 diff -B -u -r --new-file psqlodbc-08.00.0102.old/socket.c psqlodbc-08.00.0102.new/socket.c --- psqlodbc-08.00.0102.old/socket.c 2005-03-02 16:11:08.000000000 +0100 +++ psqlodbc-08.00.0102.new/socket.c 2005-08-10 15:17:53.000000000 +0200 @@ -101,19 +101,150 @@ if (self->buffer_out) free(self->buffer_out); - if (self->sadr != (struct sockaddr *) &(self->sadr_in)) + if (self->sadr != (struct sockaddr *) &(self->ss)) free(self->sadr); free(self); } +static int +SOCK_connect_to_inet_address(SocketClass *self) +{ + if (!self->sadr) + return 0; + + if ((self->socket = socket(self->sadr->sa_family, SOCK_STREAM, 0))==-1) + { + self->errornumber = SOCKET_COULD_NOT_CREATE_SOCKET; + self->errormsg = "Could not create Socket."; + return 0; + } +#ifdef TCP_NODELAY + else + { + int i = 1; + if (setsockopt(self->socket, IPPROTO_TCP, TCP_NODELAY, (char *) &i, sizeof(i)) < 0) + { + self->errornumber = SOCKET_COULD_NOT_CONNECT; + self->errormsg = "Could not set socket to NODELAY."; + closesocket(self->socket); + self->socket = (SOCKETFD) - 1; + return 0; + } + } +#endif + if (connect(self->socket, self->sadr, self->sadr_len) < 0) + { + self->errornumber = SOCKET_COULD_NOT_CONNECT; + self->errormsg = "Could not connect to remote socket."; + closesocket(self->socket); + self->socket = (SOCKETFD) - 1; + return 0; + } + return 1; +} -char -SOCK_connect_to(SocketClass *self, unsigned short port, char *hostname #ifdef HAVE_UNIX_SOCKETS - , char *uds /* unix domain socket path */ +static int +SOCK_connect_to_UNIX(SocketClass *self, unsigned short port, char *hostname, char *uds) +{ + struct sockaddr_un *un; + + /* + * In sockaddr_storage should be enough space all types of addresses, but + * maybe some other systems use a variable sized sockaddr_un to hold a + * bigger pathname... ???* -- kzak + */ + un = (struct sockaddr_un *) malloc(sizeof(struct sockaddr_un)); + if (!un) + { + self->errornumber = SOCKET_COULD_NOT_CREATE_SOCKET; + self->errormsg = "coulnd't allocate memory for un."; + return 0; + } + un->sun_family = AF_UNIX; + UNIXSOCK_PATH(un, port, uds); + self->sadr_len = UNIXSOCK_LEN(un); + self->sadr = (struct sockaddr *) un; + + self->socket = socket(AF_UNIX, SOCK_STREAM, 0); + if (self->socket == -1) + { + self->errornumber = SOCKET_COULD_NOT_CREATE_SOCKET; + self->errormsg = "Could not create Socket."; + return 0; + } + if (connect(self->socket, self->sadr, self->sadr_len) < 0) + { + self->errornumber = SOCKET_COULD_NOT_CONNECT; + self->errormsg = "Could not connect to remote socket."; + closesocket(self->socket); + self->socket = (SOCKETFD) - 1; + return 0; + } + return 1; +} #endif - ) + +#ifdef HAVE_GETADDRINFO +/* + * This version is based on getaddrinfo(), it means thread safe, AF_INET and AF_INET6... + * -- hans-Juergen Schoenig <hs@cybertec.at> (10-Aug-2005) + */ +static int +SOCK_connect_to_INET(SocketClass *self, unsigned short port, char *hostname) +{ + struct addrinfo hints; + struct addrinfo *res = NULL; + + if (hostname==NULL || *hostname=='\0') + return 0; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + + if (getaddrinfo(hostname, NULL, &hints, &res)==0) + { + while (res) + { + /* initialize address pointer */ + self->sadr = (struct sockaddr *) &self->ss; +#ifdef HAVE_IPV6 + if (res->ai_family==AF_INET6) + { + self->sadr_len = sizeof(struct sockaddr_in6); + memcpy(self->sadr, res->ai_addr, self->sadr_len); + ((struct sockaddr_in6 *) self->sadr)->sin6_port = htons(port); + } + else +#endif + if (res->ai_family==AF_INET) + { + self->sadr_len = sizeof(struct sockaddr_in6); + memcpy(self->sadr, res->ai_addr, self->sadr_len); + ((struct sockaddr_in *) self->sadr)->sin_port = htons(port); + } + if (SOCK_connect_to_inet_address(self)) + return 1; + + /* well, let's try next possible address */ + res = res->ai_next; + } + } + self->errornumber = SOCKET_HOST_NOT_FOUND; + self->errormsg = "Could not resolve hostname."; + return 0; +} + +#else /* !HAVE_GETADDRINFO */ + +/* + * Classic version without getaddrinfo() and without ipv6 support + * ... deprecated on modern OS! + */ +static int +SOCK_connect_to_INET(SocketClass *self, unsigned short port, char *hostname) { #if defined (POSIX_MULTITHREAD_SUPPORT) const int bufsz = 8192; @@ -125,129 +256,100 @@ struct hostent* hp; #endif /* POSIX_MULTITHREAD_SUPPORT */ struct sockaddr_in *in; -#ifdef HAVE_UNIX_SOCKETS - struct sockaddr_un *un; -#endif /* HAVE_UNIX_SOCKETS */ int family, sLen; #ifdef WIN32 UInt4 iaddr; #else in_addr_t iaddr; #endif - - if (self->socket != -1) - { - self->errornumber = SOCKET_ALREADY_CONNECTED; - self->errormsg = "Socket is already connected"; + + if (hostname==NULL || *hostname=='\0') return 0; - } - - /* - * If it is a valid IP address, use it. Otherwise use AF_UNIX socket. - */ - if (hostname && hostname[0]) + iaddr = inet_addr(hostname); + + self->sadr = (struct sockaddr *) &self->ss; + self->sadr_len = sizeof(struct sockaddr_in); + + in = (struct sockaddr_in *) &self->ss; + in->sin_family = family = AF_INET; + in->sin_port = htons(port); + + if (iaddr == INADDR_NONE) { - iaddr = inet_addr(hostname); - memset((char *) &(self->sadr_in), 0, sizeof(self->sadr_in)); - in = &(self->sadr_in); - in->sin_family = family = AF_INET; - in->sin_port = htons(port); - sLen = sizeof(self->sadr_in); - if (iaddr == INADDR_NONE) - { #if defined (POSIX_MULTITHREAD_SUPPORT) - #if defined (HAVE_GETIPNODEBYNAME) /* Free-BSD ? */ - hp = getipnodebyname(hostname, AF_INET, 0, &error); - #elif defined (PGS_REENTRANT_API_1) /* solaris, irix */ - hp = gethostbyname_r(hostname, hp, buf, bufsz, &error); - #elif defined (PGS_REENTRANT_API_2) /* linux */ - int result = 0; - result = gethostbyname_r(hostname, hp, buf, bufsz, &hp, &error); - if (result) - hp = NULL; - #else - hp = gethostbyname(hostname); - #endif /* HAVE_GETIPNODEBYNAME */ +#if defined (HAVE_GETIPNODEBYNAME) /* Free-BSD ? */ + hp = getipnodebyname(hostname, AF_INET, 0, &error); +#elif defined (PGS_REENTRANT_API_1) /* solaris, irix */ + hp = gethostbyname_r(hostname, hp, buf, bufsz, &error); +#elif defined (PGS_REENTRANT_API_2) /* linux */ + int result = 0; + result = gethostbyname_r(hostname, hp, buf, bufsz, &hp, &error); + if (result) + hp = NULL; #else - hp = gethostbyname(hostname); -#endif /* POSIX_MULTITHREAD_SUPPORT */ - if (hp == NULL) - { - self->errornumber = SOCKET_HOST_NOT_FOUND; - self->errormsg = "Could not resolve hostname."; - return 0; - } - memcpy(&(in->sin_addr), hp->h_addr, hp->h_length); - } - else - memcpy(&(in->sin_addr), (struct in_addr *) & iaddr, sizeof(iaddr)); - self->sadr = (struct sockaddr *) in; - -#if defined (HAVE_GETIPNODEBYNAME) - freehostent(hp); + hp = gethostbyname(hostname); #endif /* HAVE_GETIPNODEBYNAME */ - } - else -#ifdef HAVE_UNIX_SOCKETS - { - un = (struct sockaddr_un *) malloc(sizeof(struct sockaddr_un)); - if (!un) +#else + hp = gethostbyname(hostname); +#endif /* POSIX_MULTITHREAD_SUPPORT */ + if (hp == NULL) { - self->errornumber = SOCKET_COULD_NOT_CREATE_SOCKET; - self->errormsg = "coulnd't allocate memory for un."; + self->errornumber = SOCKET_HOST_NOT_FOUND; + self->errormsg = "Could not resolve hostname."; return 0; } - un->sun_family = family = AF_UNIX; - /* passing NULL means that this only suports the pg default "/tmp" */ - UNIXSOCK_PATH(un, port, uds); - sLen = UNIXSOCK_LEN(un); - self->sadr = (struct sockaddr *) un; - } -#else - { - self->errornumber = SOCKET_HOST_NOT_FOUND; - self->errormsg = "Hostname isn't specified."; - return 0; + memcpy(&(in->sin_addr), hp->h_addr, hp->h_length); } -#endif /* HAVE_UNIX_SOCKETS */ + else + memcpy(&(in->sin_addr), (struct in_addr *) & iaddr, sizeof(iaddr)); - self->socket = socket(family, SOCK_STREAM, 0); - if (self->socket == -1) +#if defined (HAVE_GETIPNODEBYNAME) + freehostent(hp); +#endif /* HAVE_GETIPNODEBYNAME */ + + return SOCK_connect_to_inet_address(self); +} + +#endif /* HAVE_GETADDRINFO */ + +/* + * Main of AF_UNIX, AF_INET and AF_INET6 connection + */ +char +SOCK_connect_to(SocketClass *self, unsigned short port, char *hostname +#ifdef HAVE_UNIX_SOCKETS + , char *uds /* unix domain socket path */ +#endif + ) +{ + if (self->socket != -1) { - self->errornumber = SOCKET_COULD_NOT_CREATE_SOCKET; - self->errormsg = "Could not create Socket."; + self->errornumber = SOCKET_ALREADY_CONNECTED; + self->errormsg = "Socket is already connected"; return 0; } -#ifdef TCP_NODELAY - if (family == AF_INET) - { - int i, len; - i = 1; - len = sizeof(i); - if (setsockopt(self->socket, IPPROTO_TCP, TCP_NODELAY, (char *) &i, len) < 0) - { - self->errornumber = SOCKET_COULD_NOT_CONNECT; - self->errormsg = "Could not set socket to NODELAY."; - closesocket(self->socket); - self->socket = (SOCKETFD) - 1; - return 0; - } - } -#endif /* TCP_NODELAY */ + /* zeroize sockaddr storage */ + memset((char *) &self->ss, 0, sizeof(self->ss)); - self->sadr_len = sLen; - if (connect(self->socket, self->sadr, sLen) < 0) + /* + * AF_UNIX or nothing... + */ + if (hostname==NULL || *hostname=='\0') +#ifdef HAVE_UNIX_SOCKETS + return SOCK_connect_to_UNIX(self, port, hostname, uds); +#else { - self->errornumber = SOCKET_COULD_NOT_CONNECT; - self->errormsg = "Could not connect to remote socket."; - closesocket(self->socket); - self->socket = (SOCKETFD) - 1; + self->errornumber = SOCKET_HOST_NOT_FOUND; + self->errormsg = "Hostname isn't specified."; return 0; } - - return 1; +#endif + /* + * AF_INET + */ + return SOCK_connect_to_INET(self, port, hostname); } diff -B -u -r --new-file psqlodbc-08.00.0102.old/socket.h psqlodbc-08.00.0102.new/socket.h --- psqlodbc-08.00.0102.old/socket.h 2005-03-02 16:11:09.000000000 +0100 +++ psqlodbc-08.00.0102.new/socket.h 2005-08-10 15:20:28.000000000 +0200 @@ -55,6 +55,35 @@ #define SOCKET_GET_INT_WRONG_LENGTH 9 #define SOCKET_CLOSED 10 +/* + * sockaddr storage -- see glibc or RFC2553 + */ +#ifndef HAVE_STRUCT_SOCKADDR_STORAGE + +/* Desired design of maximum size and alignment */ +#define _SS_MAXSIZE 128 /* Implementation specific max size */ +#define _SS_ALIGNSIZE (sizeof (int64_t)) /* Implementation specific desired alignment */ + +/* Definitions used for sockaddr_storage structure paddings design. */ +#define _SS_PAD1SIZE (_SS_ALIGNSIZE - sizeof (sa_family_t)) +#define _SS_PAD2SIZE (_SS_MAXSIZE - (sizeof (sa_family_t)+_SS_PAD1SIZE+_SS_ALIGNSIZE)) + +struct sockaddr_storage +{ + sa_family_t __ss_family; /* address family */ + /* Following fields are implementation specific */ + char __ss_pad1[_SS_PAD1SIZE]; + /* 6 byte pad, this is to make implementation + * specific pad up to alignment field that + * follows explicit in the data structure */ + int64_t __ss_align; /* field to force desired structure */ + /* storage alignment */ + char __ss_pad2[_SS_PAD2SIZE]; + /* 112 byte pad to achieve desired size, */ + /* _SS_MAXSIZE value minus size of ss_family */ + /* __ss_pad1, __ss_align fields is 112 */ +}; +#endif /* !HAVE_STRUCT_SOCKADDR_STORAGE */ struct SocketClass_ { @@ -68,11 +97,12 @@ SOCKETFD socket; - char *errormsg; - int errornumber; - struct sockaddr *sadr; /* Used for handling connections for cancel */ - int sadr_len; - struct sockaddr_in sadr_in; /* Used for INET connections */ + char *errormsg; + int errornumber; + + struct sockaddr_storage ss; /* generic address IPV4, IPV6, UNIX... */ + struct sockaddr *sadr; /* pointer to .ss or new allocated space for AF_UNIX */ + int sadr_len; char reverse; /* used to handle Postgres 6.2 protocol * (reverse byte order) */ diff -B -u -r --new-file psqlodbc-08.00.0102.old/tests/Makefile psqlodbc-08.00.0102.new/tests/Makefile --- psqlodbc-08.00.0102.old/tests/Makefile 1970-01-01 01:00:00.000000000 +0100 +++ psqlodbc-08.00.0102.new/tests/Makefile 2005-08-09 15:37:33.000000000 +0200 @@ -0,0 +1,23 @@ + +PROGS = psqlodbc_connect + +override CFLAGS += `odbc_config --cflags` +override LDFLAGS += `odbc_config --libs` + + +all: $(PROGS) + +psqlodbc_connect: psqlodbc_connect.c + $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) + +dep depend: + $(CC) -MM $(CPPFLAGS) *.c >depend + +clean: + rm -f $(PROGS) + +ifeq (depend,$(wildcard depend)) +include depend +endif + + diff -B -u -r --new-file psqlodbc-08.00.0102.old/tests/psqlodbc_connect.c psqlodbc-08.00.0102.new/tests/psqlodbc_connect.c --- psqlodbc-08.00.0102.old/tests/psqlodbc_connect.c 1970-01-01 01:00:00.000000000 +0100 +++ psqlodbc-08.00.0102.new/tests/psqlodbc_connect.c 2005-08-09 15:42:19.000000000 +0200 @@ -0,0 +1,100 @@ +/* + * ===================================================================================== + * + * Filename: psqlodbc_test1.c + * + * Description: PostgreSQL ODBC tests: connection to server + * + * Version: $Id$ + * Created: 09.08.2005 12:57:51 CEST + * + * Author: Hans-Juergen Schoenig <hs@cybertec.at> + * + * ===================================================================================== + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sql.h> +#include <sqlext.h> + +static void +extract_error(char *fn, SQLHANDLE handle, SQLSMALLINT type) +{ + SQLINTEGER i = 0; + SQLINTEGER native; + SQLCHAR state[ 7 ]; + SQLCHAR text[256]; + SQLSMALLINT len; + SQLRETURN ret; + + fprintf(stderr,"The driver reported the following diagnostics: %s:\n", fn); + do + { + ret = SQLGetDiagRec(type, handle, ++i, state, &native, + text, sizeof(text), &len ); + if (SQL_SUCCEEDED(ret)) + printf("\t%s:%ld:%ld:%s\n", state, i, native, text); + } while( ret == SQL_SUCCESS ); +} + +int +main(int argc, char **argv) +{ + SQLCHAR *constr = NULL; + SQLHENV env; + SQLHDBC dbc; + SQLHSTMT stmt; + SQLRETURN ret; + SQLCHAR outstr[BUFSIZ]; + SQLSMALLINT outstrlen; + SQLCHAR dbms_name[500], dbms_ver[500]; + + if (argc > 1 && (strcmp(argv[1], "--help")==0 || strcmp(argv[1], "-h")==0)) + { + fprintf(stderr, "usage: %s [<connection-sting>]\n" + " default connection string: 'DSN=PostgreSQL;'\n\n", + argv[0]); + exit(EXIT_FAILURE); + } + constr = (SQLCHAR *) (argc < 2 ? "DSN=PostgreSQL;" : argv[1]); + + SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &env); + SQLSetEnvAttr(env, SQL_ATTR_ODBC_VERSION, (void *) SQL_OV_ODBC3, 0); + + SQLAllocHandle(SQL_HANDLE_DBC, env, &dbc); + + ret = SQLDriverConnect(dbc, (void *)1, constr, SQL_NTS, outstr, + sizeof(outstr), &outstrlen, SQL_DRIVER_COMPLETE); + + printf("Connect string: %s\n", outstr); + + if (!SQL_SUCCEEDED(ret)) + { + fprintf(stderr, "Failed to connect\n"); + extract_error("SQLDriverConnect", dbc, SQL_HANDLE_DBC); + + SQLFreeHandle(SQL_HANDLE_DBC, dbc); + SQLFreeHandle(SQL_HANDLE_ENV, env); + + exit(EXIT_FAILURE); + } + + printf("Connected\n"); + if (ret == SQL_SUCCESS_WITH_INFO) + extract_error("SQLDriverConnect", dbc, SQL_HANDLE_DBC); + + SQLGetInfo(dbc, SQL_DBMS_NAME, (SQLPOINTER)dbms_name, sizeof(dbms_name), NULL); + SQLGetInfo(dbc, SQL_DBMS_VER, (SQLPOINTER)dbms_ver, sizeof(dbms_ver), NULL); + + printf("DBMS Name: %s\n", dbms_name); + printf("DBMS Version: %s\n", dbms_ver); + + SQLDisconnect(dbc); + SQLFreeHandle(SQL_HANDLE_DBC, dbc); + SQLFreeHandle(SQL_HANDLE_ENV, env); + + exit(EXIT_SUCCESS); +} +
pgsql-odbc by date: