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);
+}
+